zoukankan      html  css  js  c++  java
  • Noip2016 总结&反思

    一直在期盼的联赛,真正来临时,却远不像我想象的样子。

    有些事,真的不敢再想。

    算法可以离线,时光却不能倒流。dfs可以回溯,现实却没有如果。

    有些事,注定只能成为缺憾,抱恨终生。

     

    不得不说今年Noip画风大变。

    一是题目难度不循常规。D1难度如此之大,估计很多人都会很紧张。(比如我,下了场以为要有一群250+的,感觉自己要AFO了。)并且,D1T2居然比D1T3还难(D2也是如此),把好多人给坑了(估计很多人都是死磕T2不放结果T3的76分暴力都没时间写了)。

    PS:感觉题目难度应该是这样的,D1T1<D2T1<D1T3<D2T3<D2T2<D1T2

    二是题目类型有所变化。今年的题基本没看见模板题,都是一些比较考察思维的题。

    “综合来看,相比去年的题目偏重考察知识点,今年的题目在思维难度上有了明显的提升,可以有效的避免“东西学得多,分就高”的情况。在我看来,NOIP 虽然是一个普及形式的比赛,涉及知识点也有限,但并不意味着东西学得多就能 AK,竞赛竞赛,竞的是思维,赛的是心态,而不是仅仅拘泥于学习知识点的多少、学习时间的长短。让所有人都有收获,这可能是我心中比赛应有的样子吧。”——Sengxian

    D1T2成功把自己坑了,考场上思路飘逸到树剖树分治和主席树,连一道树上差分都想不出来,都是因为思维能力太垃圾。还有D2T2(是怪我没学过Huffman树不知道用队列做的方法么……)。

     

    联赛比的确实是思维和心态。

    自己心态也比较差,考场上没能想出D1T2(放到平常考试估计想个1h也能想出来……),暴力还写挂了,分数-=15;D1T3本来想到正解了,然而脑残的以为最后一维要开3,死活过不去(真是SB,我为什么不试试开2会怎样);D2T1都能飘逸到Lucas定理,真是醉了;一直在叮嘱自己注意卡常,结果D2T2的make_heap()众给我贡献了2T,分数-=10;D2T3一眼看上去居然看成了搜索,明明不会剪枝还硬剪了半个小时,真是废了(放到平常考试估计很快就能看出状压的……)。

    至于思维,向来是我的弱项。以前总是在学一些高端的东西,平衡树、主席树、树套树、CDQ……然而这些东西联赛都没能用上(平时最实用的主席树到了这儿也没能用上……),反而学了这些东西之后做了好多模板题,对思维的锻炼就很少了。

    联赛的6道题,D1T1和D2T1两道水题水过去了,D2T3一道状压傻逼题卡过去了,D1T2的树上差分、D1T3的期望DP和D2T2的队列都没想出来,写的暴力。

    一场考试,6道题,我居然写了3个暴力,真是废了。幸亏暴力分给的足,要不然我早就AFO了。

    现在离省选也没多少天了。唯一期盼的就是黑暗的高考生活赶快结束,WC和省选集训早日到来。

     

    言归正传。

     

    D1T1 玩具谜题

    水题不解释。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 using namespace std;
     5 const int maxn=100010;
     6 int n,m,a[maxn],x,d,cur=0;
     7 char s[maxn][15];
     8 int main(){
     9     scanf("%d%d",&n,&m);
    10     scanf("%d%s",&a[0],s[0]);
    11     for(int i=n-1;i;i--)scanf("%d%s",&a[i],s[i]);
    12     while(m--){
    13         scanf("%d%d",&x,&d);
    14         if(x==a[cur])cur=(cur+d)%n;
    15         else cur=(cur-d+n)%n;
    16     }
    17     printf(s[cur]);
    18     return 0;
    19 }
    View Code

     

    D1T2 天天爱跑步

    感觉这种题就该放在D2T3……

    首先把每条路径(u,v)拆成两条路径(u,LCA)和(LCA,v)后分别考虑,然后对于每个x,考虑哪些点会对ans[x]产生贡献。

    设每个点的深度为d,显然只有满足以下条件的u和v会对x的答案产生贡献:

    (对u来说)d[u]-d[x]=w[x]

    (对v来说)d[u]-d[LCA]+d[x]-d[LCA]=w[x]

    把式子变形,得

    d[u]=w[x]+d[x]

    d[u]-2*d[LCA]=w[x]-d[x]

    这样左边的值就不随x而改变,因为只有单点修改单点查询,用两个数组分别维护即可。

    显然对于一个点x,只有(u,v)有一个在x的子树中的路径才能对ans[x]产生贡献。因此我们可以用树上差分,每个修改在u和v处进行,在LCA处撤销,在dfs到一个点之前记录答案,与dfs结束后的答案相减即为子树中的贡献之和。

    可能是我写的比较渣,如果对LCA处有贡献的话会算重一次,需要手动去重。

    LCA我写的是树剖,后来重写一遍Tarjan结果写挂了,算了反正树剖也挺快我就这么着吧……

    细节问题:

    a[]维护u的贡献,b[]维护v的贡献。

    因为所有u的贡献都一样,直接压缩到cntu[]中表示这个点作为u的次数即可。

    u[]是对LCA记录所有u(因为v的贡献只与u有关,所以不用记录v到底是谁),v[]是对v记录所有u和LCA。

    因为d[u]-2*d[LCA]可能是负的,因此加上一个2*n的偏移量。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #include<vector>
     5 using namespace std;
     6 const int maxn=300010;
     7 void dfs1(int);
     8 void dfs2(int);
     9 int LCA(int,int);
    10 void dfs(int);
    11 int prt[maxn],size[maxn],d[maxn],son[maxn],top[maxn];
    12 vector<int>G[maxn],u[maxn];
    13 struct pir{int u,lca;pir(int u,int lca):u(u),lca(lca){}};
    14 vector<pir>v[maxn];
    15 int n,m,w[maxn],cntu[maxn]={0},ans[maxn]={0},x,y;
    16 int a[maxn]={0},b[maxn<<2]={0};
    17 int main(){
    18     scanf("%d%d",&n,&m);
    19     for(int i=1;i<n;i++){
    20         scanf("%d%d",&x,&y);
    21         G[x].push_back(y);
    22         G[y].push_back(x);
    23     }
    24     dfs1(1);dfs2(1);
    25     for(int i=1;i<=n;i++)scanf("%d",&w[i]);
    26     while(m--){
    27         scanf("%d%d",&x,&y);
    28         int lca=LCA(x,y);
    29         cntu[x]++;
    30         u[lca].push_back(x);
    31         v[y].push_back(pir(x,lca));
    32         if(d[lca]+w[lca]==d[x])ans[lca]--;
    33     }
    34     dfs(1);
    35     for(int i=1;i<=n;i++)printf("%d ",ans[i]);
    36     return 0;
    37 }
    38 void dfs1(int x){
    39     size[x]=1;
    40     for(int i=0;i<(int)G[x].size();i++)if(G[x][i]!=prt[x]){
    41         prt[G[x][i]]=x;
    42         d[G[x][i]]=d[x]+1;
    43         dfs1(G[x][i]);
    44         size[x]+=size[G[x][i]];
    45         if(size[G[x][i]]>size[son[x]])son[x]=G[x][i];
    46     }
    47 }
    48 void dfs2(int x){
    49     if(x==son[prt[x]])top[x]=top[prt[x]];
    50     else top[x]=x;
    51     for(int i=0;i<(int)G[x].size();i++)if(G[x][i]!=prt[x])dfs2(G[x][i]);
    52 }
    53 int LCA(int x,int y){
    54     while(top[x]!=top[y]){
    55         if(d[top[x]]<d[top[y]])swap(x,y);
    56         x=prt[top[x]];
    57     }
    58     return d[x]<d[y]?x:y;
    59 }
    60 void dfs(int x){
    61     ans[x]-=a[d[x]+w[x]]+b[w[x]-d[x]+(n<<1)];
    62     a[d[x]]+=cntu[x];
    63     for(int i=0;i<(int)v[x].size();i++)b[d[v[x][i].u]-(d[v[x][i].lca]<<1)+(n<<1)]++;
    64     for(int i=0;i<(int)G[x].size();i++)if(G[x][i]!=prt[x])dfs(G[x][i]);
    65     ans[x]+=a[d[x]+w[x]]+b[w[x]-d[x]+(n<<1)];
    66     for(int i=0;i<(int)u[x].size();i++){
    67         a[d[u[x][i]]]--;
    68         b[d[u[x][i]]-(d[x]<<1)+(n<<1)]--;
    69     }
    70 }
    View Code

    反思:

    这题在考场上把我坑了0.5+h,当时什么都想,偏偏想不出来正解……加之前一天晚上睡得很不踏实,有点困,最终还是没想出来,打的60分暴力。结果有15分写挂了,真是废了……

    反映出自己思维能力过弱,碰到稍微有点难度的题就想不出来。

    一定要少刷模板题。

     

    D1T3 换教室

    期望DP模板题,我特么居然跪了……

    设f[i][j][0/1]表示前i节课选了j节,0/1表示这节课选了没有的期望值。手玩各种转移就行了。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 using namespace std;
     5 const int maxn=2010,maxv=310;
     6 int n,m,v,e,c[maxn],d[maxn],dis[maxn][maxn],x,y,z;
     7 double p[maxn],f[maxn][maxn][2],ans=1e50;
     8 int main(){
     9     memset(dis,63,sizeof(dis));
    10     scanf("%d%d%d%d",&n,&m,&v,&e);
    11     m=min(n,m);
    12     for(int i=1;i<=v;i++)dis[i][i]=0;
    13     for(int i=1;i<=n;i++)scanf("%d",&c[i]);
    14     for(int i=1;i<=n;i++)scanf("%d",&d[i]);
    15     for(int i=1;i<=n;i++)scanf("%lf",&p[i]);
    16     while(e--){
    17         scanf("%d%d%d",&x,&y,&z);
    18         if(z<dis[x][y])dis[x][y]=dis[y][x]=z;
    19     }
    20     for(int k=1;k<=v;k++)for(int i=1;i<=v;i++)for(int j=1;j<=v;j++)dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
    21     for(int i=0;i<=n;i++)for(int j=0;j<=m;j++)f[i][j][0]=f[i][j][1]=1e50;
    22     f[1][0][0]=f[1][1][1]=0.0;
    23     for(int i=2;i<=n;i++)for(int j=0;j<=i&&j<=m;j++){
    24         f[i][j][0]=min(f[i-1][j][0]+(double)dis[c[i-1]][c[i]],f[i-1][j][1]+p[i-1]*dis[d[i-1]][c[i]]+(1.0-p[i-1])*dis[c[i-1]][c[i]]);
    25         if(j>0)f[i][j][1]=min(f[i-1][j-1][0]+p[i]*dis[c[i-1]][d[i]]+(1.0-p[i])*dis[c[i-1]][c[i]],f[i-1][j-1][1]+p[i-1]*p[i]*dis[d[i-1]][d[i]]+(1.0-p[i-1])*p[i]*dis[c[i-1]][d[i]]+p[i-1]*(1.0-p[i])*dis[d[i-1]][c[i]]+(1.0-p[i-1])*(1.0-p[i])*dis[c[i-1]][c[i]]);
    26         else f[i][j][1]=1e50;
    27     }
    28     for(int j=0;j<=m;j++)ans=min(ans,min(f[n][j][0],f[n][j][1]));
    29     printf("%.2lf",ans);
    30     return 0;
    31 }
    View Code

    反思:

    考场上想到了0/1,然后我脑残的以为应该分没选、选了成功和选了失败三种情况讨论,结果死调不出来,还是写的m<=2的76分暴力,这次倒是没写挂……

    第一次好好写期望DP,被干穿了……自己弱不能怪社会……

     

    D2T1 组合数问题

    杨辉三角+二维前缀和随便水。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 using namespace std;
     5 const int maxn=2010;
     6 int q,n,m,p,c[maxn][maxn]={{0}},a[maxn][maxn]={{0}};
     7 int main(){
     8     scanf("%d%d",&q,&p);
     9     c[0][0]=1;
    10     for(int i=1;i<=2000;i++){
    11         c[i][0]=1;
    12         for(int j=1;j<=i;j++){
    13             c[i][j]=(c[i-1][j]+c[i-1][j-1])%p;
    14             if(!c[i][j])a[i][j]=1;
    15         }
    16     }
    17     for(int i=1;i<=2000;i++){
    18         a[i][0]+=a[i-1][0];
    19         a[0][i]+=a[0][i-1];
    20         for(int j=1;j<=2000;j++)a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];
    21     }
    22     while(q--){
    23         scanf("%d%d",&n,&m);
    24         printf("%d
    ",a[n][m]);
    25     }
    26     return 0;
    27 }
    View Code

    反思:

    考场上思路有点飘逸,结果费了10min……虽然费的时间不算多吧……

     

    D2T2 蚯蚓

    首先把所有蚯蚓变长改成切出来的蚯蚓变短,然后用三个队列分别维护原来的蚯蚓、砍出来的长的那段和短的那段,显然三个队列都是单调的,这样就可以线性了。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #include<functional>
     5 #include<queue>
     6 #define frt(que) ((que.empty()?-2147483647:que.front()))
     7 using namespace std;
     8 int n,m,t,u,v,q,o[100010],x,a,b,c;
     9 queue<int>qa,qb,qc;
    10 int main(){
    11     scanf("%d%d%d%d%d%d",&n,&m,&q,&u,&v,&t);
    12     for(int i=1;i<=n;i++)scanf("%d",&o[i]);
    13     sort(o+1,o+n+1,greater<int>());
    14     for(int i=1;i<=n;i++)qa.push(o[i]);
    15     for(int i=1;i<=m;i++){
    16         a=frt(qa);
    17         b=frt(qb);
    18         c=frt(qc);
    19         if(a>=b&&a>=c)qa.pop();
    20         else if(b>c){
    21             a=b;
    22             qb.pop();
    23         }
    24         else{
    25             a=c;
    26             qc.pop();
    27         }
    28         a+=q*(i-1);
    29         x=(long long)a*u/v;
    30         qb.push(x-q*i);
    31         qc.push(a-x-q*i);
    32         if(i%t==0)printf("%d ",a);
    33     }
    34     printf("
    ");
    35     for(int i=1;!(qa.empty()&&qb.empty()&&qc.empty());i++){
    36         a=frt(qa);
    37         b=frt(qb);
    38         c=frt(qc);
    39         if(a>=b&&a>=c)qa.pop();
    40         else if(b>c){
    41             a=b;
    42             qb.pop();
    43         }
    44         else{
    45             a=c;
    46             qc.pop();
    47         }
    48         a+=q*m;
    49         if(i%t==0)printf("%d ",a);
    50     }
    51     return 0;
    52 }
    View Code

    反思:

    可能是没见过这种题吧,当时只想到了堆乱搞。也从一个侧面反映出自己做题太少,何况很多没见过这种题的也想到队列了呢……

     

    D2T3 愤怒的小鸟

    状压傻逼题。

    话说各种被uoj hack,一开始eps设成1e-7被hack了,改成1e-8又被hack了,改成1e-9还被hack了,改成1e-10就A了,uoj这hack数据也太丧病了吧……

    那个cnt_tbl是用来统计1数量卡边界用的,话说这玩意儿还挺好使……

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<cmath>
     4 #include<algorithm>
     5 #define popcnt(x) (cnt_tbl[(x)>>16]+cnt_tbl[(x)&65535])
     6 using namespace std;
     7 const int maxn=25;
     8 const double eps=1e-10;
     9 double dcmp(double);
    10 double x[maxn],y[maxn];
    11 int T,n,f[1<<19],cnt_tbl[70010]={0};
    12 int main(){
    13     for(int i=1;i<65536;i++)cnt_tbl[i]=cnt_tbl[i>>1]+(i&1);
    14     scanf("%d",&T);
    15     while(T--){
    16         memset(f,63,sizeof(f));
    17         for(int s=0;s<(1<<18);s++)f[s]=popcnt(s);
    18         scanf("%d%*d",&n);
    19         for(int i=1;i<=n;i++)scanf("%lf%lf",&x[i],&y[i]);
    20         for(int i=1;i<=n;i++)for(int j=1;j<i;j++)if(dcmp(x[i]-x[j])){
    21             double a=(x[j]*y[i]-x[i]*y[j])/(x[i]*x[j]*(x[i]-x[j])),b=(x[j]*x[j]*y[i]-x[i]*x[i]*y[j])/(x[i]*x[j]*(x[j]-x[i]));
    22             if(dcmp(a)>-1)continue;
    23             int w=0;
    24             for(int k=1;k<=n;k++)if(!dcmp(a*x[k]*x[k]+b*x[k]-y[k]))w|=(1<<(k-1));
    25             for(int s=0;s<(1<<n);s++)f[s|w]=min(f[s|w],f[s]+1);
    26         }
    27         printf("%d
    ",f[(1<<n)-1]);
    28     }
    29     return 0;
    30 }
    31 double dcmp(double x){
    32     if(fabs(x)<eps)return 0;
    33     return x<0.0?-1:1;
    34 }
    View Code

    反思:

    第一眼看成了搜索,尝试剪枝浪费了很多时间。自己弱不能怪社会。

     

    这次联赛,算是对自己一年多OI生涯的检验。结果自己却挂了,失误很多。(不知道这些到底该说是失误还是能力问题。)

    别的,真的不想再说什么了。不敢再想。

    很快就是WC,然后就是省选。结果如何,只能看自己了。

  • 相关阅读:
    Windows消息初探(1)
    配置Git Extension免密码发布代码到CSDN
    一个简单的参数校验类
    Functions类,一个Javascript的函数加法类,将两个函数加起来,顺序执行
    对Javascript异步执行的理解
    aws在线技术峰会笔记-游戏解决方案
    aws在线技术峰会笔记-主会场
    努力成长为一只全栈工程师
    翻译:在Ubuntu 14.04上安装FTP服务器的方法
    奇妙的时间旅程
  • 原文地址:https://www.cnblogs.com/hzoier/p/6219716.html
Copyright © 2011-2022 走看看