2019CCPC秦皇岛自我反省&部分题解
练了一年半了,第一次打CCPC,险些把队友坑了打铁,最后也是3题危险捡了块铜。
非常水的点双连通,我居然不相信自己去相信板子,唉,结果整来整去,本来半个小时能出的题,整到了3个小时,大失误呀,不然就可以去弄其他题了,对自己的表现太失望了,给自己记一大过。然后导致了策略失误,稀里糊涂的时间久过去了,不然感觉我们队还是能再出3题的。
可惜可惜,自己的这些大赛的经验还是不足,但相信这不会成为拖累自己队伍的后腿,发现问题就要及时调整,在赛场上还是要坚信自己所想到的,不用太苛求于模板形式。
有些急躁,但心态还是挺好,下次多喝点水,急的时候就去尿尿冷静一下。
之前思维题都由鲲鲲和+1很快就想出来了,想不出来就没得法子,自己在思维上明显退步了,接下来除了算法的学习,更重要的思维也不能忘了,自己去补一补。
之后几场不知道会怎么样,未来不可想,现在犹可抓,好好把握接下来不多的时间。
Stand by,Stand by.
1004 就看是不是只包含2和5
1 #include<cstdio> 2 bool judge(int n){ 3 while(n>1){ 4 if(n%5==0) n/=5; 5 else if(n%2==0) n/=2; 6 else return false; 7 } 8 return true; 9 } 10 int main(){ 11 int t,n; 12 scanf("%d",&t); 13 while(t--){ 14 scanf("%d",&n); 15 if(judge(n)) printf("No\n"); 16 else printf("Yes\n"); 17 } 18 return 0; 19 }
1006 就看每个环的贡献,就这个sb了,套了个点双的板子,发现错误没及时改。后来重现时是单组测试,所以把多组去了
1 #include<cstdio> 2 #include<stack> 3 #include<algorithm> 4 using namespace std; 5 typedef long long ll; 6 const int N=5e5+11,M=5e5+11; 7 const ll md=998244353; 8 struct Side{ 9 int v,ne; 10 Side(){} 11 Side(int v,int ne):v(v),ne(ne){} 12 }S[M<<1]; 13 Side sta[M]; 14 int n,sn,dn,bn,tn,head[N],dfn[N],low[N],bin[N],bsi[N]; 15 ll cf2[M]; 16 void init(){ 17 sn=dn=bn=tn=0; 18 for(int i=0;i<=n;i++){ 19 head[i]=-1; 20 dfn[i]=bin[i]=0; 21 } 22 } 23 void add(int u,int v){ 24 S[sn].v=v; 25 S[sn].ne=head[u]; 26 head[u]=sn++; 27 } 28 void pdc(int u,int fa){ 29 dfn[u]=low[u]=++dn; 30 for(int i=head[u],v;~i;i=S[i].ne){ 31 v=S[i].v; 32 if(!dfn[v]){ 33 sta[++tn]=Side(u,v); 34 pdc(v,u); 35 low[u]=min(low[u],low[v]); 36 if(low[v]>=dfn[u]){ 37 int su,sv; 38 bsi[++bn]=0; 39 while(tn){ 40 su=sta[tn].v;sv=sta[tn].ne; 41 tn--; 42 if(su==u&&sv==v) break; 43 if(bin[su]!=bn){ 44 bin[su]=bn; 45 bsi[bn]++; 46 } 47 if(bin[sv]!=bn){ 48 bin[sv]=bn; 49 bsi[bn]++; 50 } 51 } 52 } 53 }else if(v!=fa){ 54 low[u]=min(low[u],dfn[v]); 55 if(dfn[u]>dfn[v]) sta[++tn]=Side(u,v); 56 } 57 } 58 } 59 int main(){ 60 cf2[0]=1; 61 for(int i=1;i<M;i++){ 62 cf2[i]=cf2[i-1]<<1; 63 if(cf2[i]>=md) cf2[i]%=md; 64 } 65 int t,m,u,v; 66 //scanf("%d",&t); 67 //while(t--){ 68 scanf("%d%d",&n,&m); 69 init(); 70 for(int i=0;i<m;i++){ 71 scanf("%d%d",&u,&v); 72 add(u,v); 73 add(v,u); 74 } 75 ll ans=1; 76 for(int i=1;i<=n;i++) if(!dfn[i]) pdc(i,i); 77 for(int i=1;i<=bn;i++){ 78 if(!bsi[i]) continue; 79 ans*=((cf2[bsi[i]]-1)%md+md)%md; 80 if(ans>=md) ans%=md; 81 m-=bsi[i]; 82 } 83 ans*=cf2[m]; 84 if(ans>=md) ans%=md; 85 printf("%lld\n",ans); 86 // } 87 return 0; 88 }
其实直接dfs找环就行了。
1 #include<cstdio> 2 #include<stack> 3 #include<algorithm> 4 using namespace std; 5 typedef long long ll; 6 const int N=5e5+11,M=5e5+11; 7 const ll md=998244353; 8 struct Side{ 9 int v,ne; 10 Side(){} 11 Side(int v,int ne):v(v),ne(ne){} 12 }S[M<<1]; 13 Side sta[M]; 14 int n,m,sn,dn,bn,tn,head[N],vis[N],dep[N]; 15 ll ans,cf2[M]; 16 void init(){ 17 sn=dn=bn=tn=0; 18 for(int i=0;i<=n;i++){ 19 head[i]=-1; 20 vis[i]=0; 21 } 22 } 23 void add(int u,int v){ 24 S[sn].v=v; 25 S[sn].ne=head[u]; 26 head[u]=sn++; 27 } 28 void dfs(int u,int fa,int d){ 29 dep[u]=d; 30 vis[u]=1; 31 for(int i=head[u],v;~i;i=S[i].ne){ 32 v=S[i].v; 33 if(v==fa||vis[v]==2) continue; 34 if(vis[v]){ 35 m-=d-dep[v]+1; 36 ans*=(cf2[d-dep[v]+1]-1+md)%md; 37 if(ans>=md) ans%=md; 38 }else dfs(v,u,d+1); 39 } 40 vis[u]=2; 41 } 42 int main(){ 43 cf2[0]=1; 44 for(int i=1;i<M;i++){ 45 cf2[i]=cf2[i-1]<<1; 46 if(cf2[i]>=md) cf2[i]%=md; 47 } 48 int t,u,v; 49 scanf("%d%d",&n,&m); 50 init(); 51 for(int i=0;i<m;i++){ 52 scanf("%d%d",&u,&v); 53 add(u,v); 54 add(v,u); 55 } 56 ans=1; 57 for(int i=1;i<=n;i++) if(!vis[i]) dfs(i,i,0); 58 ans*=cf2[m]; 59 if(ans>=md) ans%=md; 60 printf("%lld\n",ans); 61 return 0; 62 }
1009 一道dp题,当时是+1做的,自己补了下。就每个技能最多有6种组合,dp[i][j]就是第i个技能,是用第j个组合按出来的,最少方案数。然后预处理一下,两两技能不同组合之间转移时需要多按的键数便可。
1 #include<cstdio> 2 #include<tr1/unordered_map> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const char jn[15][10][10]={ 7 {{"QQQ"},{"QQQ"},{"QQQ"},{"QQQ"},{"QQQ"},{"QQQ"}}, 8 {{"QQW"},{"WQQ"},{"QWQ"},{"QQW"},{"WQQ"},{"QWQ"}}, 9 {{"QQE"},{"EQQ"},{"QEQ"},{"QQE"},{"EQQ"},{"QEQ"}}, 10 {{"WWW"},{"WWW"},{"WWW"},{"WWW"},{"WWW"},{"WWW"}}, 11 {{"QWW"},{"WQW"},{"WWQ"},{"QWW"},{"WQW"},{"WWQ"}}, 12 {{"WWE"},{"EWW"},{"WEW"},{"WWE"},{"EWW"},{"WEW"}}, 13 {{"EEE"},{"EEE"},{"EEE"},{"EEE"},{"EEE"},{"EEE"}}, 14 {{"QEE"},{"EQE"},{"EEQ"},{"QEE"},{"EQE"},{"EEQ"}}, 15 {{"WEE"},{"EWE"},{"EEW"},{"WEE"},{"EWE"},{"EEW"}}, 16 {{"QWE"},{"QEW"},{"WQE"},{"WEQ"},{"EWQ"},{"EQW"}}, 17 }; 18 const int N=1e5+11,inf=1e9+7; 19 int val[15][15][15][15],dp[N][15]; 20 char s[N]; 21 tr1::unordered_map<char,int> mmp; 22 void init(){ 23 mmp['Y']=0;mmp['V']=1;mmp['G']=2;mmp['C']=3;mmp['X']=4; 24 mmp['Z']=5;mmp['T']=6;mmp['F']=7;mmp['D']=8;mmp['B']=9; 25 for(int i=0;i<10;i++) 26 for(int j=0;j<10;j++) 27 for(int k=0;k<6;k++) 28 for(int l=0;l<6;l++){ 29 for(int q=0,p;q<=3;q++){ 30 for(p=q;p<3;p++) 31 if(jn[i][k][p]!=jn[j][l][p-q]) break; 32 if(p>=3){ 33 val[i][j][k][l]=q; 34 break; 35 } 36 } 37 } 38 } 39 int main(){ 40 init(); 41 while(~scanf("%s",s)){ 42 int lens=strlen(s); 43 for(int i=0;i<lens;i++) 44 for(int j=0;j<6;j++) dp[i][j]=inf; 45 for(int i=0;i<6;i++) dp[0][i]=4; 46 for(int i=1;i<lens;i++){ 47 int j1=mmp[s[i-1]],j2=mmp[s[i]]; 48 for(int k1=0;k1<6;k1++) 49 for(int k2=0;k2<6;k2++) 50 dp[i][k2]=min(dp[i][k2],dp[i-1][k1]+val[j1][j2][k1][k2]+1); 51 } 52 int ans=inf; 53 for(int i=0;i<6;i++) ans=min(ans,dp[lens-1][i]); 54 printf("%d\n",ans); 55 } 56 return 0; 57 }
1010 哎,字符串循环节水题,当时忘了,就没做这题。我们需要知道的任意长度后缀里的循环节长度是多少,那倒序求一遍kmp即可。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 typedef long long ll; 6 const int N=1e7+11; 7 const ll inf=1e18+7; 8 char s1[N],s2[N]; 9 int ne[N]; 10 void getne(char *s){ 11 int i=0,j=-1,lens=strlen(s); 12 ne[0]=-1; 13 while(i<lens){ 14 if(j==-1||s[i]==s[j]) ne[++i]=++j; 15 else j=ne[j]; 16 } 17 } 18 int main(){ 19 int a,b; 20 while(~scanf("%d%d%s",&a,&b,s1)){ 21 int lens1=strlen(s1),lens2; 22 for(int i=0;i<lens1;i++){ 23 if(s1[lens1-1-i]=='.'){ 24 s2[i]='\0'; 25 break; 26 } 27 s2[i]=s1[lens1-1-i]; 28 } 29 getne(s2); 30 lens2=strlen(s2); 31 ll ans=-inf; 32 for(int i=1;i<=lens2;i++){ 33 int len=i-ne[i]; 34 ans=max(ans,1ll*a*i-1ll*b*len); 35 } 36 printf("%lld\n",ans); 37 } 38 return 0; 39 }
1005 我第一道看的就是这题,当时就觉得是网络流能做,可惜当时做的人不多,就没开。干就完事了。然后因为任意两个机器人肯定不能走同样的路径,也就是每个位置的水平和垂直方向只能走一次,所以就是每个
位置抄成两个点,然后转向器无非就是在每个位置的水平和垂直的两点间连边,然后再把机器人与相应位置的水平方向连边,终点与相应位置的垂直方向连边,然后跑一遍,看最大流是不是等于机器人数就好了。
1 #include<cstdio> 2 #include<queue> 3 using namespace std; 4 const int N=2e4+11,inf=1e9+7; 5 struct Side{ 6 int v,w,ne; 7 }S[N<<2]; 8 char mp[111][111]; 9 int sb,se,sn,head[N],dep[N],cur[N]; 10 void init(){ 11 sn=0; 12 for(int i=sb;i<=se;i++) head[i]=-1; 13 } 14 void add(int u,int v,int w){ 15 S[sn].v=v; 16 S[sn].w=w; 17 S[sn].ne=head[u]; 18 head[u]=sn++; 19 } 20 void addE(int u,int v,int w,int op){ 21 add(u,v,w); 22 add(v,u,op ? 0 : w ); 23 } 24 bool bfs(){ 25 for(int i=sb;i<=se;i++) dep[i]=0; 26 queue<int> q; 27 dep[sb]=1; 28 q.push(sb); 29 int u,v; 30 while(!q.empty()){ 31 u=q.front(); 32 q.pop(); 33 for(int i=head[u];~i;i=S[i].ne){ 34 v=S[i].v; 35 if(S[i].w>0&&!dep[v]){ 36 dep[v]=dep[u]+1; 37 if(v==se) return true; 38 q.push(v); 39 } 40 } 41 } 42 return false; 43 } 44 int dfs(int u,int minf){ 45 if(!minf||u==se) return minf; 46 int v,flow; 47 for(int &i=cur[u];~i;i=S[i].ne){ 48 v=S[i].v; 49 if(S[i].w>0&&dep[v]==dep[u]+1){ 50 flow=dfs(v,min(minf,S[i].w)); 51 if(flow){ 52 S[i].w-=flow; 53 S[i^1].w+=flow; 54 return flow; 55 } 56 } 57 } 58 return 0; 59 } 60 int dinic(){ 61 int maxf=0,flow; 62 while(bfs()){ 63 for(int i=sb;i<=se;i++) cur[i]=head[i]; 64 while(flow=dfs(sb,inf)) maxf+=flow; 65 } 66 return maxf; 67 } 68 int main(){ 69 int t,n,m,a,b,x; 70 scanf("%d",&t); 71 while(t--){ 72 scanf("%d%d%d%d",&n,&m,&a,&b); 73 sb=0;se=2*n*m+1; 74 init(); 75 for(int i=0;i<n;i++){ 76 scanf("%s",mp[i]); 77 for(int j=0;j<m;j++){ 78 //printf("%d %d\n",i*m+j+1,n*m+i*m+j+1); 79 if(mp[i][j]=='1') continue; 80 addE(i*m+j+1,n*m+i*m+j+1,1,0); 81 if(i&&mp[i-1][j]!='1') addE(i*m+j+1,(i-1)*m+j+1,1,0); 82 if(j&&mp[i][j-1]!='1') addE(n*m+i*m+j+1,n*m+i*m+j,1,0); 83 } 84 } 85 for(int i=1;i<=a;i++){ 86 scanf("%d",&x); 87 if(mp[0][x-1]!='1') addE(sb,x,1,1); 88 } 89 for(int i=1;i<=b;i++){ 90 scanf("%d",&x); 91 if(mp[n-1][x-1]!='1') addE((n-1)*m+x,se,1,1); 92 } 93 if(dinic()!=a) printf("No\n"); 94 else printf("Yes\n"); 95 } 96 return 0; 97 }
1001 当时鲲鲲已经做出来了,可惜卡常数,最后重现才补出来。https://blog.csdn.net/mmk27_word/article/details/101638730
1011 +1最后思路已经来了,跟题解类似,可惜没敲完。(大小姐是不写博客的)
能出的题还是挺多的,下次把所有可惜没做到的,转换成做到的。