zoukankan      html  css  js  c++  java
  • [NOIP补坑计划]NOIP2016 题解&做题心得

    感觉16年好难啊QAQ,两天的T2T3是不是都放反了啊……

    场上预计得分:100+80+100+100+65+100=545(省一分数线280)

    ps:loj没有部分分,部分分见洛咕

    题解:

    D1T1 玩具谜题

    题面

    水题送温暖~当然主要关注点都在mengbier和mogician上2333

     1 #include<algorithm>
     2 #include<iostream>
     3 #include<cstring>
     4 #include<cstdio>
     5 #include<cmath>
     6 #include<queue>
     7 #define inf 2147483647
     8 #define eps 1e-9
     9 using namespace std;
    10 typedef long long ll;
    11 int n,m,nw=0,x,op,a[100001];
    12 char s[100001][11];
    13 int main(){
    14     scanf("%d%d",&n,&m);
    15     for(int i=0;i<n;i++){
    16         scanf("%d%s",&a[i],s[i]);
    17     }
    18     for(int i=1;i<=m;i++){
    19         scanf("%d%d",&op,&x);
    20         if(op^a[nw])nw=(nw+x)%n;
    21         else nw=(nw+n-x)%n;
    22     }
    23     printf("%s",s[nw]);
    24     return 0;
    25 }

    D1T2 天天爱跑步

    题面

    绝对是这六题里最难的一题……这题目难度与顺序无关啊……想了半小时只会80,yy了一个线段树合并调不出来,树上差分并不会,最后百度了一发才搞懂……

    首先考虑S->T这一条路径,肯定是从S出发先向上走到lca,再向下走到T,那么分开讨论这两种情况;

    1.S->lca,此时路径上能对答案产生贡献的点i要满足$dep[i]+w[i]=dep[s]$;

    2.lca->T,此时路径上能对答案产生贡献的点i要满足$dep[i]-w[i]=dep[t]-L$(其中L表示S到T的总长度)

    这里貌似可以树链剖分+线段树直接维护?但是我出于对出题人基本的信任并没有写……

    正解是用桶维护深度,维护一个向上的桶u和一个向下的桶d,对整棵树dfs;

    对于一条s到t的路径,扫到s时就在s的$u[dep[s]]$中加一个,lca退栈时就把影响减掉,扫到lca时就在$d[dep[t]]$中加一个,t退栈时就减掉(这就是树上差分的思想);

    那么每次的答案$ans[i]$就等于子树中$u[dep[i]+w[i]]+d[dep[i]-w[i]]$的数量;

    注意这里可能会有负数,所以要整体加上$MAXN$;

    最后如果s到t是一条链那么答案会算重,要把答案减一;

      1 #include<algorithm>
      2 #include<iostream>
      3 #include<cstring>
      4 #include<cstdio>
      5 #include<cmath>
      6 #include<queue>
      7 #define inf 2147483647
      8 #define eps 1e-9
      9 #define MX 300001
     10 using namespace std;
     11 typedef long long ll;
     12 struct edge{
     13     int v,next;
     14 }a[600001];
     15 int n,m,u,v,l,tot=0,fa[300001][20],ans[300001],bb[600001],bs[300001],bg[300001],dep[300001],ss[300001],t[300001],z[300001],num[300001],head[300001];
     16 vector<int>s[300001],t1[300001],t2[300001];
     17 void add(int u,int v){
     18     a[++tot].v=v;
     19     a[tot].next=head[u];
     20     head[u]=tot;
     21 }
     22 void dfs(int u,int ff,int dpt){
     23     fa[u][0]=ff;
     24     dep[u]=dpt;
     25     for(int i=1;i<=19;i++)fa[u][i]=fa[fa[u][i-1]][i-1];
     26     for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
     27         int v=a[tmp].v;
     28         if(v!=ff){
     29             dfs(v,u,dpt+1);
     30         }
     31     }
     32 }
     33 int lca(int u,int v){
     34     if(dep[u]<dep[v])swap(u,v);
     35     int l=dep[u]-dep[v];
     36     for(int i=19;i>=0;i--){
     37         if((1<<i)&l){
     38             u=fa[u][i];
     39         }
     40     }
     41     if(u==v)return u;
     42     for(int i=19;i>=0;i--){
     43         if(fa[u][i]!=fa[v][i]){
     44             u=fa[u][i],v=fa[v][i];
     45         }
     46     }
     47     return fa[u][0];
     48 }
     49 void dfs1(int u,int ff){
     50     int nw=bs[dep[u]+num[u]];
     51     for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
     52         int v=a[tmp].v;
     53         if(v!=ff){
     54             dfs1(v,u);
     55         }
     56     }
     57     bs[dep[u]]+=bg[u];
     58     ans[u]+=bs[dep[u]+num[u]]-nw;
     59     for(int i=0,ii=s[u].size();i<ii;i++){
     60         bs[s[u][i]]--;
     61     }
     62 }
     63 void dfs2(int u,int ff){
     64     int nw=bb[dep[u]-num[u]+MX];
     65     for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
     66         int v=a[tmp].v;
     67         if(v!=ff){
     68             dfs2(v,u);
     69         }
     70     }
     71     for(int i=0,ii=t1[u].size();i<ii;i++){
     72         bb[t1[u][i]+MX]++;
     73     }
     74     ans[u]+=bb[dep[u]-num[u]+MX]-nw;
     75     for(int i=0,ii=t2[u].size();i<ii;i++){
     76         bb[t2[u][i]+MX]--;
     77     }
     78 }
     79 int main(){
     80     memset(head,-1,sizeof(head));
     81     scanf("%d%d",&n,&m);
     82     for(int i=1;i<n;i++){
     83         scanf("%d%d",&u,&v);
     84         add(u,v);
     85         add(v,u);
     86     }
     87     for(int i=1;i<=n;i++){
     88         scanf("%d",&num[i]);
     89     }
     90     dfs(1,0,1);
     91     for(int i=1;i<=m;i++){
     92         scanf("%d%d",&ss[i],&t[i]);
     93         z[i]=lca(ss[i],t[i]);
     94         l=dep[ss[i]]+dep[t[i]]-2*dep[z[i]];
     95         bg[ss[i]]++;
     96         s[z[i]].push_back(dep[ss[i]]);
     97         t1[t[i]].push_back(dep[t[i]]-l);
     98         t2[z[i]].push_back(dep[t[i]]-l);
     99     }
    100     dfs1(1,0);
    101     dfs2(1,0);
    102     for(int i=1;i<=m;i++){
    103         if(dep[z[i]]+num[z[i]]==dep[ss[i]])ans[z[i]]--;
    104     }
    105     for(int i=1;i<=n;i++){
    106         printf("%d ",ans[i]);
    107     }
    108     return 0;
    109 }

    D1T3 换教室

    题面

    题面看起来很复杂,实际上并不难……

    显然这是个期望dp,且路程期望具有可加性,那么设$f[i][j][0 / 1]$表示当前在第i个时间段,已经申请了j次,这次是否申请;

    推出式子就好了……

    设$dis[i][j]$表示i到j的最短路:

    $f[i][j][0]=min{f[i-1][j][0]+dis[c[i-1]][c[i]],f[i-1][j][1]+dis[c[i-1]][c[i]] imes(1-k[i-1])$

    $+dis[d[i-1]][c[i]] imes k[i-1]}$

    $f[i][j][1]=min{f[i-1][j-1][0]+dis[c[i-1]][c[i]] imes(1-k[i])+dis[c[i-1]][d[i]] imes k[i],$

    $f[i-1][j-1][1]+dis[c[i-1]][c[i]] imes(1-k[i-1]) imes(1-k[i])+dis[d[i-1]][c[i]] imes k[i-1] imes(1-k[i])$

    $+dis[c[i-1]][d[i]] imes(1-k[i-1]) imes k[i]+dis[d[i-1]][d[i]] imes k[i-1] imes k[i]}$

    由于v很小,所以可以直接用floyd求出所有点对之间的最短路,于是就做完了……

    时间复杂度$O(nm+V^3)$

     1 #include<algorithm>
     2 #include<iostream>
     3 #include<cstring>
     4 #include<cstdio>
     5 #include<cmath>
     6 using namespace std;
     7 int n,m,v,e,u,vv,w,c[2001],d[2001],sp[2001][2001];
     8 double k[2000001],f[2001][2001][2],tt,ans;
     9 int main(){
    10     memset(sp,0x3f,sizeof(sp));
    11     scanf("%d%d%d%d",&n,&m,&v,&e);
    12     for(int i=1;i<=n;i++){
    13         scanf("%d",&c[i]);
    14     }
    15     for(int i=1;i<=n;i++){
    16         scanf("%d",&d[i]);
    17     }
    18     for(int i=1;i<=n;i++){
    19         scanf("%lf",&k[i]);
    20     }
    21     for(int i=0;i<=v;i++){
    22         sp[i][i]=0;
    23     }
    24     for(int i=0;i<=n;i++){
    25         for(int j=0;j<=m;j++){
    26             f[i][j][0]=1e16;
    27             f[i][j][1]=1e16;
    28         }
    29     } 
    30     f[1][0][0]=f[1][1][1]=0;
    31     for(int i=1;i<=e;i++){
    32         scanf("%d%d%d",&u,&vv,&w);
    33         if(u==vv){
    34             continue;
    35         }
    36         sp[u][vv]=min(sp[u][vv],w);
    37         sp[vv][u]=sp[u][vv];
    38     }
    39     for(int kk=1;kk<=v;kk++){
    40         for(int i=1;i<=v;i++){
    41             for(int j=1;j<=v;j++){
    42                 sp[i][j]=min(sp[i][kk]+sp[kk][j],sp[i][j]);
    43             }
    44         }
    45     }
    46     for(int i=2;i<=n;i++){
    47         f[i][0][0]=f[i-1][0][0]+sp[c[i-1]][c[i]];
    48         for(int j=1;j<=min(m,i);j++){            
    49             f[i][j][0]=min(f[i-1][j][0]+sp[c[i-1]][c[i]],f[i-1][j][1]+sp[c[i-1]][c[i]]*(1.0-k[i-1])+sp[d[i-1]][c[i]]*k[i-1]);
    50             f[i][j][1]=f[i-1][j-1][0]+sp[c[i-1]][c[i]]*(1.0-k[i])+sp[c[i-1]][d[i]]*k[i];
    51             tt=f[i-1][j-1][1]+sp[c[i-1]][c[i]]*(1.0-k[i-1])*(1.0-k[i])+sp[d[i-1]][c[i]]*k[i-1]*(1.0-k[i])+sp[c[i-1]][d[i]]*(1.0-k[i-1])*k[i]+sp[d[i-1]][d[i]]*k[i-1]*k[i];
    52             f[i][j][1]=min(f[i][j][1],tt); 
    53         }
    54     }
    55     ans=f[n][0][0];
    56     for(int i=1;i<=m;i++){
    57         ans=min(ans,min(f[n][i][1],f[n][i][0]));
    58     }
    59     printf("%.2lf",ans);
    60     return 0;
    61 }

    D2T1 组合数问题

    题面

    并不是很水?要预处理前缀和;

     1 #include<algorithm>
     2 #include<iostream>
     3 #include<cstring>
     4 #include<cstdio>
     5 #include<cmath>
     6 #include<queue>
     7 #define inf 2147483647
     8 #define eps 1e-9
     9 using namespace std;
    10 typedef long long ll;
    11 int n,m,t,k,s[2001][2001],C[2001][2001];
    12 int main(){
    13     scanf("%d%d",&t,&k);
    14     C[0][0]=1;
    15     for(int i=1;i<=2000;i++){
    16         C[i][0]=1;
    17         for(int j=1;j<=i;j++){
    18             C[i][j]=(C[i-1][j]+C[i-1][j-1])%k;
    19         }
    20     }
    21     for(int i=1;i<=2000;i++){
    22         for(int j=1;j<=2000;j++){
    23             s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+(!C[i][j]&&i>=j);
    24         }
    25     }
    26     while(t--){
    27         scanf("%d%d",&n,&m);
    28         printf("%d
    ",s[n][m]);
    29     }
    30     return 0;
    31 }

    D2T2 蚯蚓

    题面

    谁能给我解释一下这个鬼畜的部分分……

     


    这题的难点在于阅读理解……题目写的很复杂,要仔细理解题意;

    场上看到$M=7 imes 10^6$直接虚掉……写了个优先队列65分滚粗了QAQ

    注意到每次操作完整体增加q不好处理,因此可以考虑把q累加起来,每次计算完答案再加上总和,把分出来的两个数减去q即可,这样子做每次把分出来的数丢进优先队列就能拿到65分;

    正解是非常巧妙的单调队列;

    注意到这样子做所有没被拆分过的蚯蚓的长度是单调不增的,考虑被拆出来的两条新蚯蚓,由于拆分的两段比例都相等,所以较长那段和较短那段的长度也是单调不增的;

    这样子可以只在开始排序一遍,开三个单调队列来分别维护$x$,$lfloor px floor$和$x-lfloor px floor$,每次选队头最大的进行操作即可;

    想起来复杂,代码很短……

    时间复杂度$O(nlogn+m)$

     1 #include<algorithm>
     2 #include<iostream>
     3 #include<cstring>
     4 #include<cstdio>
     5 #include<cmath>
     6 #include<queue>
     7 #define inf 1000000000000000
     8 #define eps 1e-9
     9 using namespace std;
    10 typedef long long ll;
    11 int n,m,q,t,l1=1,l2=1,l3=1,r1,r2=0,r3=0,id,num[100001],q1[100001],q2[7000001],q3[7000001];
    12 ll t1,t2,u,v,ans,sum=0;
    13 bool cmp(int a,int b){
    14     return a>b;
    15 }
    16 int main(){
    17     scanf("%d%d%d%lld%lld%d",&n,&m,&q,&u,&v,&t);
    18     r1=n;
    19     for(int i=1;i<=n;i++){
    20         scanf("%d",&q1[i]);
    21     }
    22     sort(q1+1,q1+n+1,cmp);
    23     for(int i=1;i<=m;i++){
    24         id=0;
    25         ans=-inf;
    26         if(l1<=r1&&q1[l1]>ans){
    27             ans=q1[l1];
    28             id=1;
    29         }
    30         if(l2<=r2&&q2[l2]>ans){
    31             ans=q2[l2];
    32             id=2;
    33         }
    34         if(l3<=r3&&q3[l3]>ans){
    35             ans=q3[l3];
    36             id=3;
    37         }
    38         ans+=sum;
    39         if(i%t==0)printf("%lld ",ans);
    40         sum+=q;
    41         t1=ans*u/v;
    42         t2=ans-t1;
    43         q2[++r2]=t1-sum;
    44         q3[++r3]=t2-sum;
    45         if(id==1)l1++;
    46         else if(id==2)l2++;
    47         else l3++;
    48     }
    49     puts("");
    50     for(int i=1;i<=n+m;i++){
    51         id=0;
    52         ans=-inf;
    53         if(l1<=r1&&q1[l1]>ans){
    54             ans=q1[l1];
    55             id=1;
    56         }
    57         if(l2<=r2&&q2[l2]>ans){
    58             ans=q2[l2];
    59             id=2;
    60         }
    61         if(l3<=r3&&q3[l3]>ans){
    62             ans=q3[l3];
    63             id=3;
    64         }
    65         if(i%t==0)printf("%lld ",ans+sum);
    66         if(id==1)l1++;
    67         else if(id==2)l2++;
    68         else l3++;
    69     }
    70     return 0;
    71 }

    D2T3 愤怒的小鸟

    题面

    暴力状压DP可过……(可能在考数学?)

    预处理$g[i][j]$表示打第$i$和第$j$只猪的抛物线能打到哪些猪,然后状压表示当前打死了哪些猪;

    显然$f[s|g[i][j]]=min{f[s|g[i][j]],f[s]+1}$

    时间复杂度$O(n^22^n)$

    ps:公式如下:

    $y_1=ax_{1}^{2}+bx_1$

    $y_2=ax_{2}^{2}+bx_2$

    则$a=frac{y_1x_2-x_1y_2}{x_1x_2(x_1-x_2)}$,$b=frac{x_{1}^{2}y_2-x_{2}^{2}y_1}{x_1x_2(x_1-x_2)}$

     1 #include<algorithm>
     2 #include<iostream>
     3 #include<cstring>
     4 #include<cstdio>
     5 #include<cmath>
     6 #include<queue>
     7 #define inf 0x7f7f7f7f
     8 #define eps 1e-9
     9 using namespace std;
    10 typedef long long ll;
    11 struct pt{
    12     double x,y;
    13 }p[21];
    14 int t,n,m,f[300001],nxt[21][21];
    15 void pre(){
    16     memset(nxt,0,sizeof(nxt));
    17     for(int i=1;i<n;i++){
    18         for(int j=i+1;j<=n;j++){
    19             double x=p[i].x,y=p[i].y,xx=p[j].x,yy=p[j].y;
    20             if(fabs(x-xx)<eps)continue;
    21             double a=(y*xx-x*yy)/(x*xx*(x-xx)),b=(x*x*yy-xx*xx*y)/(x*xx*(x-xx));
    22             if(a>-eps)continue;
    23             for(int k=1;k<=n;k++){
    24                 double _x=p[k].x,_y=p[k].y;
    25                 if(fabs(a*_x*_x+b*_x-_y)<eps)nxt[i][j]|=(1<<k-1); 
    26             }
    27         }
    28     }
    29 }
    30 void work(){
    31     memset(f,0x7f,sizeof(f));
    32     f[0]=0;
    33     for(int s=0;s<(1<<n);s++){
    34         if(f[s]!=inf){
    35             for(int i=1;i<=n;i++){
    36                 for(int j=i+1;j<=n;j++){
    37                     f[s|nxt[i][j]]=min(f[s|nxt[i][j]],f[s]+1);
    38                 }
    39                 f[s|(1<<i-1)]=min(f[s|(1<<i-1)],f[s]+1);
    40             }
    41         }
    42     }
    43 }
    44 int main(){
    45     scanf("%d",&t);
    46     while(t--){
    47         scanf("%d%d",&n,&m);
    48         for(int i=1;i<=n;i++){
    49             scanf("%lf%lf",&p[i].x,&p[i].y);
    50         }
    51         pre();
    52         work();
    53         printf("%d
    ",f[(1<<n)-1]);
    54     }
    55     return 0;
    56 }

    总结:

    我太菜了.jpg

  • 相关阅读:
    java.io.Serializable浅析
    SSH和SSM的区别
    [转]github详细教程
    GITHUB的使用
    常用端口-小结
    DNS的解析原理
    windows快捷键-小结
    ip地址0.0.0.0是什么意思
    windows插件框架下载地址
    redis和mongodb
  • 原文地址:https://www.cnblogs.com/dcdcbigbig/p/9927081.html
Copyright © 2011-2022 走看看