1. 程式人生 > >[NOIP補坑計劃]NOIP2015 題解&做題心得

[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)log\sum 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年的題明顯感覺到了差別,大力切題爽爽