A. Vasya And Password
链接:http://codeforces.com/contest/1051/problem/A
题目大意:给你一段字符串,要求你对其中连续的一段进行修改,以使得这个字符串同时包含大小写字母和数字,输出修改后的字符串。
解法:
首先检查原串里出现了几种字符:
1.出现一种,只要将前两个字符修改为没有出现的字符。
2.出现两种,找一个位置的字符进行修改为没有出现过的字符,并且要确认这个字符不是某个种类唯一的一个。
但是实现起来还是有一定难度
#include<cstdio> #include<cstring> char s[105]; int main(){ int t; scanf("%d",&t); while(t--){ scanf("%s",s); int l=strlen(s),a1=0,a2=0,a3=0,b1=0,b2=0,b3=0; int mi=l; for(int i=0;i<l;++i) if(s[i]>='0'&&s[i]<='9') ++a1,b1=1; else if(s[i]>='a'&&s[i]<='z') ++a2,b2=1; else a3++,b3=1; if(b1+b2+b3==3) printf("%s\n",s); else if(b1+b2+b3==2){ if(!b1){ if((s[0]>='a'&&s[0]<='z'&&a2!=1)||(s[0]>='A'&&s[0]<='Z'&&a3!=1)) s[0]='1'; else s[1]='1'; } else if(!b2){ if((s[0]>='0'&&s[0]<='9'&&a1!=1)||(s[0]>='A'&&s[0]<='Z'&&a3!=1)) s[0]='a'; else s[1]='a'; } else if(!b3){ if((s[0]>='0'&&s[0]<='9'&&a1!=1)||(s[0]>='a'&&s[0]<='z'&&a2!=1)) s[0]='A'; else s[1]='A'; } printf("%s\n",s); } else{ if(a1) s[0]='a',s[1]='A'; else if(a2) s[0]='1',s[1]='A'; else s[0]='1',s[1]='a'; printf("%s\n",s); } } return 0; }
B. Relatively Prime Pairs
链接:http://codeforces.com/contest/1051/problem/B
题目大意:给你一段区间[l,r],这段区间内的所有整数组成一个集合,把集合中的所有整数两两分为一组,(集合元素数一定是偶数)要保证每一组的gcd均为1,若能输出Yes和分组情况(多解输出任意一个合法解),若不能输出no
解法:
首先不可能输出no
因为i和i+1一定能够分为一组,
反证法:假设a=gcd(i,i+1)>1那么i=b*a,i+1=c*a => 1=(c-b)*a 因为a,b,c均为整数所以该式子显然不成立
然后就以上述方法分组就可以了
#include<cstdio> int main(){ long long l,r; scanf("%I64d%I64d",&l,&r); printf("YES\n"); for(long long i=l;i<=r;++i){ printf("%I64d ",i); if((i-l)%2) printf("\n"); } return 0; }
C. Vasya and Multisets
链接:http://codeforces.com/contest/1051/problem/C
题目大意:给定n个整数,把它们分成两个部分每个部分中数量为1的元素种数相同。能则输出Yes和分割情况,不能则输出No
解法:
首先用桶记录所有数,
我们可以忽略那些数量为2的数,因为它们不会影响答案
对于数量为1的数,如果有偶数个那么也可以使得两个部分平衡
但是如果是奇数个那么就需要一个数量s>2的数划分为s-1,1进行平衡;
#include<cstdio> #include<vector> using namespace std; char s[105]; int t[105],a[105],tt[4]; vector<int>to[105]; int main(){ int n; scanf("%d",&n); for(int i=1;i<=n;++i){ scanf("%d",&a[i]); t[a[i]]++; to[a[i]].push_back(i); } for(int i=1;i<=100;++i) if(t[i]>2) tt[3]++; else tt[t[i]]++; if(tt[1]&1&&!tt[3]) printf("NO"); else{ printf("YES\n"); for(int i=1,j='A';i<=100;++i) if(t[i]==1){ s[to[i][0]]=j; if(j=='A') j='B'; else j='A'; } if(tt[1]&1) for(int i=1;i<=100;++i) if(t[i]>2){ s[to[i][0]]='B'; break; } for(int i=1;i<=n;++i) if(!s[i]) s[i]='A'; printf("%s",s+1); } return 0; }
D. Bicolorings
题目大意:现在有一个大小为2*N的矩阵每个点可以染成黑或白色,相邻并且颜色相同的则为同一块,问总块数==k的染色方法有多少种(对998244353取模)
解法:非常裸的dp,对于一行来说可能会有4种状态:黑黑,白白,白黑,黑白,分别标号为0设f[i][j][k]表示前i列块数为k最后一行状态为k的方案数
转移的方式,可以自己写一个矩阵,因为不同的状态之间进行转移的时候会增加的块数不同。
#include<cstdio> const int mod=998244353; int f[2][4][2005],a[4][4]={ {0,1,1,1}, {1,0,1,1}, {0,0,0,2}, {0,0,2,0} }; int main(){ int n,k,i,j,p,q,ans=0; scanf("%d%d",&n,&k); f[1][0][1]=f[1][1][1]=f[1][2][2]=f[1][3][2]=1; for(i=2;i<=n;++i) for(j=1;j<=i*2&&j<=k;++j) for(p=0;p<4;++p){ f[i&1][p][j]=0; for(q=0;q<4;++q){ f[i&1][p][j]+=f[i&1^1][q][j-a[q][p]]; if(f[i&1][p][j]>=mod) f[i&1][p][j]%=mod; } //printf("%d %d %d %d\n",i,j,p,f[i&1][p][j]); } for(p=0;p<4;++p){ ans+=f[n&1][p][k]; if(ans>=mod) ans%=mod; } printf("%d",ans); return 0; }
E. Vasya and Big Integers
题目大意:给你一个大整数a分为多段,分割的时候分出的每一段的数的范围是l,r(它们也是大整数)问你分割方案数
解法:同样也是dp,首先设f[i]表示前i个数的划分方案f[i]=sum(f[l]+...+f[r])l,r为这次划分所能取的范围。
首先使用前缀和可以将求和过程优化为O(1)
然后就是边界的确定,这个过程是指:整数a在靠后的长度与l的长度相同的一段比较,这会使右边界波动;与r的比较会使左边界波动
直接比较每次就需要将这两个大整数比较,如果有很多相同的部分就会耗时很多,因此我们需要一个预处理提前算好每一段中最大公共前缀。
首先另外去一个字符串为a+'*'+l中间的*主要是为了分割和防止过多匹配
用y[i]表示从i开始与从0开始的最多可以匹配到哪一位
记录下已经匹配的最靠右区间,然后如果目前i在这个区间就说明至少能匹配到y[i-l]。
如果不在就从0开始。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=1e6+5,M=998244353; char a[N],l[N<<1],r[N<<1]; int d[N<<1],e[N<<1],s[N],f,tf; void cl(char *x,int ll,int *y){ for(int i=1,l=0,r=0;i<ll;++i){ if(i<=r) y[i]=min(y[i-l],r-i+1); for(;i+y[i]<ll&&x[y[i]]==x[i+y[i]];++y[i]); if(r<=i+y[i]-1) l=i,r=i+y[i]-1; } } int main(){ scanf("%s%s%s",a,l,r); int l1=strlen(a),l2=strlen(l),l3=strlen(r); l[l2]=r[l3]='*'; for(int i=0;i<l1;++i) l[l2+i+1]=r[l3+i+1]=a[i]; cl(l,l1+l2+1,d); cl(r,l1+l3+1,e); for(int i=0;i<l2;++i) s[i]=1; for(int i=l2;i<=l1;++i){ int ll,rr; if(i<l3) ll=-1; else if(e[i+1]==l3||r[e[i+1]]>=a[i-l3+e[i+1]]) ll=i-l3-1; else ll=i-l3; if(d[i+1]==l2||l[d[i+1]]<=a[i-l2+d[i+1]]) rr=i-l2; else rr=i-l2-1; if(rr==-1||ll>rr); else if(ll==-1) f=s[rr]; else f=(s[rr]-s[ll]+M)%M; if(tf&&l[0]=='0'&&l2==1) f=(f+tf)%M,tf=0; s[i]=s[i-1]; if(a[i]=='0') tf=f; else s[i]=(s[i]+f)%M; } printf("%d",f); return 0; }
F. The Shortest Statement
题目大意:给你一个无向联通图,保证边数永远小于点数+20,给你q个询问,每个询问问你两个点间的最短路
解法:首先如果我们先只把n-1条边加入使其构成数,不考虑通过其他边的情况就可以lca解决
如果要通过这些边那么至少要通过其中一个点,对于每个边我们都取一个点最多21个点,在包含所有边的图中跑dijstra此时最短距离就是dis[i][u]+dis[i][v];
#include<cstdio> #include<cstring> #include<queue> using namespace std; typedef long long ll; const int N=1e5+5; struct X{ int v,f,n,p,q; }x[N<<1]; ll d[N],dis[45][N]; int fa[N],s,f[N][20],dep[N]; struct Y{ int v; ll q; bool operator<(const Y &a)const{ return q>a.q; } }; priority_queue<Y>dl; int fi(int a){ int b=a,c; while(fa[b]) b=fa[b]; while(a!=b){ c=fa[a]; fa[a]=b; a=c; } return a; } void add(int u,int v,int q,int p){ x[++s].n=x[u].f; x[x[u].f=s].q=q; x[s].v=v; x[s].p=p; } void dfs(int u){ for(int i=1;i<20;++i) f[u][i]=f[f[u][i-1]][i-1]; for(int i=x[u].f;i;i=x[i].n) if(!x[i].p&&x[i].v!=1&&!d[x[i].v]){ d[x[i].v]=d[u]+x[i].q; dep[x[i].v]=dep[u]+1; f[x[i].v][0]=u; dfs(x[i].v); } } void dij(int u){ dis[++s][u]=0; dl.push((Y){u,0}); while(dl.size()){ Y t=dl.top();dl.pop(); for(int i=x[t.v].f;i;i=x[i].n) if(dis[s][x[i].v]>x[i].q+t.q){ dis[s][x[i].v]=t.q+x[i].q; dl.push((Y){x[i].v,dis[s][x[i].v]}); } } } int main(){ int n,m,qq; scanf("%d%d",&n,&m); for(int i=1;i<=m;++i){ int u,v,q,sf; scanf("%d%d%d",&u,&v,&q); sf=fi(u)==fi(v); add(u,v,q,sf); add(v,u,q,sf); if(!sf) fa[fi(v)]=fi(u); } dfs(1);s=0; memset(dis,0x3f,sizeof(dis)); for(int i=1;i<2*m;i+=2) if(x[i].p) dij(x[i].v); scanf("%d",&qq); while(qq--){ int u,v; scanf("%d%d",&u,&v); if(dep[u]<dep[v]) swap(u,v); int c=dep[u]-dep[v],tu=u,tv=v; ll ans=d[u]+d[v]; for(int i=0;c;c>>=1,++i) if(c&1) u=f[u][i]; if(u!=v){ for(int i=19;i>=0;--i) if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i]; u=f[u][0]; } ans-=d[u]*2; for(int i=1;i<=s;++i) if(ans>dis[i][tu]+dis[i][tv]) ans=dis[i][tu]+dis[i][tv]; printf("%I64d\n",ans); } return 0; }