• [NOIP补坑计划]NOIP2015 题解&做题心得


    感觉从15年开始noip就变难了?(虽然自己都做出来了……)

    场上预计得分:100+100+60~100+100+100+100=560~600(省一分数线365)

    题解:

    D1T1 神奇的幻方

    题面

    水题送温暖~

     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,x,y,a[40][40];
    12 int main(){
    13     scanf("%d",&n);
    14     x=1,y=(n+1)/2;
    15     for(int i=1;i<=n*n;i++){
    16         a[x][y]=i;
    17         if(x==1&&y==n)x++;
    18         else if(x==1)x=n,y++;
    19         else if(y==n)x--,y=1;
    20         else if(a[x-1][y+1])x++;
    21         else x--,y++;
    22     }
    23     for(int i=1;i<=n;i++){
    24         for(int j=1;j<=n;j++){
    25             printf("%d ",a[i][j]);
    26         }
    27         puts("");
    28     }
    29     return 0;
    30 }

    D1T2 信息传递

    题面

    题意就是求最小环,可以用tarjan也可以直接用并查集;

     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,ans=inf,tmp,a[200001],fa[200001],las[200001];
    12 int ff(int u){
    13     return u==fa[u]?u:fa[u]=ff(fa[u]);
    14 }
    15 int main(){
    16     scanf("%d",&n);
    17     for(int i=1;i<=n;i++){
    18         scanf("%d",&a[i]);
    19         fa[i]=las[i]=i;
    20     }
    21     for(int i=1;i<=n;i++){
    22         int fu=ff(i),fv=ff(a[i]);
    23         if(fu==fv){
    24             tmp=0;
    25             for(int v=a[i];v!=i;v=las[v])tmp++;
    26             ans=min(ans,tmp);
    27         }else fa[fu]=fv;
    28         las[fu]=a[i];
    29     }
    30     printf("%d",ans+1);
    31     return 0;
    32 }

    D1T3 斗地主

    题面

    做了xfz那题就会这题了……场上的话不一定会写,所以期望得分60~100?

    题解见这篇博客

    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 L,n,m,l,r,ans,d[100001];
    12 bool chk(int k){
    13     int ret=0,nw=0;
    14     for(int i=1;i<=n;i++){
    15         if(d[i]-d[nw]<k)ret++;
    16         else nw=i;
    17     }
    18     return ret<=m;
    19 }
    20 int main(){
    21     scanf("%d%d%d",&L,&n,&m);
    22     for(int i=1;i<=n;i++){
    23         scanf("%d",&d[i]);
    24     }
    25     l=1,r=L;
    26     while(l<=r){
    27         int mid=(l+r)/2;
    28         if(chk(mid))ans=mid,l=mid+1;
    29         else r=mid-1;
    30     }
    31     printf("%d",ans);
    32     return 0;
    33 }

    D2T2 子串

    题面

    好题!深刻体现了我计数dp能力蒻的事实……显然设$f[i][j][k]$表示在$A$中用到第$i$位,在$B$中用到第$j$位,已经分出了$k$个子串的方案数;

    如果$A[i]=B[j]$,则可以选择当前位,那么有两种情况:

    1.继承上一位的子串,k不变;

    2.以$i$为开头新创一个子串,k++;

    直接转移不太好搞,那么再设$g[i][j][k]$表示当前必须要新创子串的方案数,和$f$一起转移;

    转移方程为$f[i][j][k]=f[i-1][j][k]+g[i][j][k],g[i][j][k]=g[i-1][j-1][k]+f[i-1][j-1][k-1]$

    直接dp会爆空间,注意到转移的时候只用到了$i$和$i-1$,用滚动数组优化即可;

     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 mod 1000000007
    10 using namespace std;
    11 typedef long long ll;
    12 int n,m,K,t=0,f[2][201][201],g[2][201][201];
    13 char a[1001],b[201];
    14 int main(){
    15     scanf("%d%d%d%s%s",&n,&m,&K,a+1,b+1);
    16     f[0][0][0]=1;
    17     for(int i=1;i<=n;i++){
    18         t^=1;
    19         f[t][0][0]=1;
    20         for(int j=1;j<=m;j++){
    21             for(int k=1;k<=K;k++){
    22                 if(a[i]==b[j]){
    23                     g[t][j][k]=(g[t^1][j-1][k]+f[t^1][j-1][k-1])%mod;
    24                 }else g[t][j][k]=0;
    25                 f[t][j][k]=(f[t^1][j][k]+g[t][j][k])%mod;
    26             }
    27         }
    28     }
    29     printf("%d",f[t][m][K]);
    30     return 0;
    31 }

    D2T3 运输计划

    题面

    看上去挺难,思路还蛮清晰的?首先显然二分答案,记录下长度大于当前答案k的路径的数量,删掉的那条边肯定在这些路径的并上,所以求出这些路径的并后找到其中最长的那条边,判最长的那条路径减去这条边后能否小于等于k即可。

    具体实现可以记录每个点的父边长度,倍增预处理每条路径,用树上差分求出路径的交,时间复杂度是$O((n+m)logsum t_i)$,正好能过?我loj第一次被卡了一个点大概50ms,加了读入优化就过了……(洛咕依旧秒过)

      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 struct edge{
     12     int v,w,next;
     13 }a[600001];
     14 int n,m,u,v,w,ans,maxn,l,r,num[300001],fr[300001],x[300001],y[300001],lca[300001],d[300001],tot=0,head[300001],dis[300001],dep[300001],fa[300001][20];
     15 char buffer[6000010],*hd,*tl;
     16 inline char Getchar(){
     17     if(hd==tl){
     18         int len=fread(buffer,1,6000009,stdin);
     19         hd=buffer,tl=hd+len;
     20         if(hd==tl)
     21             return EOF;
     22     }
     23     return *hd++;
     24 }
     25 inline int rd(){
     26     register int x=0,f=1;
     27     char c;
     28     do{
     29         c=Getchar();
     30         if(c=='-')f=-1;
     31     }while(!isdigit(c));
     32     do{
     33         x=(x<<1)+(x<<3)+(c^48);
     34         c=Getchar();
     35     }while(isdigit(c));
     36     return x*f;
     37 }
     38 void add(int u,int v,int w){
     39     a[++tot].v=v;
     40     a[tot].w=w;
     41     a[tot].next=head[u];
     42     head[u]=tot;
     43 }
     44 void dfs(int u,int ff,int dpt,int ds){
     45     dep[u]=dpt;
     46     dis[u]=ds;
     47     fa[u][0]=ff;
     48     for(int i=1;i<=19;i++){
     49         fa[u][i]=fa[fa[u][i-1]][i-1];
     50     }
     51     for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
     52         int v=a[tmp].v;
     53         if(v!=ff){
     54             fr[v]=a[tmp].w;
     55             dfs(v,u,dpt+1,ds+a[tmp].w);
     56         }
     57     }
     58 }
     59 int getlca(int u,int v){
     60     if(dep[u]<dep[v])swap(u,v);
     61     int l=dep[u]-dep[v];
     62     for(int i=19;i>=0;i--){
     63         if((1<<i)&l){
     64             u=fa[u][i];
     65         }
     66     }
     67     if(u==v)return u;
     68     for(int i=19;i>=0;i--){
     69         if(fa[u][i]!=fa[v][i]){
     70             u=fa[u][i],v=fa[v][i];
     71         }
     72     }
     73     return fa[u][0];
     74 }
     75 void getn(int u,int fa){
     76     for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
     77         int v=a[tmp].v;
     78         if(v!=fa){
     79             getn(v,u);
     80             num[u]+=num[v];
     81         }
     82     }
     83 }
     84 bool chk(int k){
     85     int cnt=0,mx=0;
     86     for(int i=1;i<=n;i++)num[i]=0;
     87     for(int i=1;i<=m;i++){
     88         if(d[i]>k){
     89             cnt++;
     90             num[x[i]]++;
     91             num[y[i]]++;
     92             num[lca[i]]-=2;
     93         }
     94     }
     95     getn(1,0);
     96     for(int i=1;i<=n;i++){
     97         if(num[i]>=cnt)mx=max(mx,fr[i]);
     98     }
     99     return maxn-mx<=k;
    100 }
    101 int main(){
    102     memset(head,-1,sizeof(head));
    103     //scanf("%d%d",&n,&m);
    104     n=rd(),m=rd();
    105     for(int i=1;i<n;i++){
    106         //scanf("%d%d%d",&u,&v,&w);
    107         u=rd(),v=rd(),w=rd();
    108         add(u,v,w);
    109         add(v,u,w);
    110     }
    111     dfs(1,0,1,0);
    112     for(int i=1;i<=m;i++){
    113         //scanf("%d%d",&x[i],&y[i]);
    114         x[i]=rd(),y[i]=rd();
    115         lca[i]=getlca(x[i],y[i]);
    116         d[i]=dis[x[i]]+dis[y[i]]-dis[lca[i]]*2;
    117         maxn=max(maxn,d[i]);
    118     }
    119     l=0,r=maxn;
    120     while(l<=r){
    121         int mid=(l+r)/2;
    122         if(chk(mid))ans=mid,r=mid-1;
    123         else l=mid+1;
    124     }
    125     printf("%d",ans);
    126     return 0;
    127 } 

    总结:

    1.这次差不多花了四个半小时做完所有题,保持速度的同时要稳,遇到难题还是要仔细思考,分析题目性质,坚信正解并不会很难

    2.计数DP(其实是所有DP)水平要加强

    3.感觉1314年真的水的厉害……做15年的题明显感觉到了差别,大力切题爽爽

  • 相关阅读:
    Office 2016 安装你所必须要注意的事项
    JetBrains软件开发框架下的类似于“.IntelliJIdea2018.1”的配置文件夹的移动
    “microsoft ace oledb 12.0 未注册”疑云
    WebStorm添加多个项目到当前工程目录
    SVN入门教程
    FreeMarker 快速入门
    ajax详解
    基于JavaMail的Java邮件发送:简单邮件发送
    css 文件上传按钮美化
    chrome开发者工具的使用
  • 原文地址:https://www.cnblogs.com/dcdcbigbig/p/9911614.html
Copyright © 2020-2023  润新知