[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年的题明显感觉到了差别,大力切题爽爽