什么垃圾比赛,A题说的什么鬼楞是没看懂。就我只会BD(其实C是个大水题二分),垃圾游戏,技不如人,肝败吓疯,告辞,口胡了E就睡觉了。
B
很容易发现,存在一种方案,使得相同字母连在一起,然后发现,当字母出现种类数大于等于4时,可以奇偶性相间地连接,然后讨论种类数<=3的:种类数为1,显然直接输出;种类数为2,若两字母相邻则无解,否则直接输出;种类数为3,若三字母相邻则无解,否则按照213/231(至少一种符合条件)输出。
#include<bits/stdc++.h> using namespace std; const int N=107; int T,n,m,ans,sum[N],id[N]; char s[N]; int main() { scanf("%d",&T); while(T--) { scanf("%s",s+1),n=strlen(s+1); memset(sum,0,sizeof sum); memset(id,0,sizeof id); for(int i=1;i<=n;i++)sum[s[i]-'a'+1]++; int num=0; for(int i=1;i<=26;i++)if(sum[i])id[i]=++num; if(num==1) { for(int i=1;i<=n;i++)printf("%c",s[i]); } else if(num>=4) { for(int i=26;i>=1;i--) if(id[i]&1) { for(int j=1;j<=sum[i];j++)printf("%c",'a'+i-1); } for(int i=26;i>=1;i--) if(id[i]&&id[i]%2==0) { for(int j=1;j<=sum[i];j++)printf("%c",'a'+i-1); } } else if(num==3) { int flag=0; for(int i=2;i<=25;i++)if(sum[i]&&sum[i-1]&&sum[i+1])flag=1; if(flag)printf("No answer"); else{ for(int i=2;i<=25;i++) for(int j=1;j<i;j++) for(int k=i+1;k<=26;k++) if(sum[j]&&sum[i]&&sum[k]) { for(int t=1;t<=sum[i];t++)printf("%c",'a'+i-1); if(j==i-1) { for(int t=1;t<=sum[k];t++)printf("%c",'a'+k-1); for(int t=1;t<=sum[j];t++)printf("%c",'a'+j-1); } else{ for(int t=1;t<=sum[j];t++)printf("%c",'a'+j-1); for(int t=1;t<=sum[k];t++)printf("%c",'a'+k-1); } } } } else{ int flag=0; for(int i=1;i<=25;i++)if(sum[i]&&sum[i+1])flag=1; if(flag)printf("No answer"); else{ for(int i=1;i<=26;i++)if(sum[i]) { for(int j=1;j<=sum[i];j++)printf("%c",'a'+i-1); } } } puts(""); } }
D
很容易想到一个DP,令f[i]表示以i为根的子树,从下面的节点走上来,最后一步是黑边的点数,g[i]表示全走白边的点数,于是就有f[u]=Σ(f[son]+g[son]+1),son为经过黑边的son,g[u]=Σ(g[son]+1),son为经过白边的son。然后这个东西很容易换根DP,根据黑白边讨论一下即可。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=2e5+7; int n,cnt,f[N],g[N],nf[N],ng[N],hd[N],v[N<<1],nxt[N<<1],w[N<<1]; ll ans; void adde(int x,int y,int z){v[++cnt]=y,nxt[cnt]=hd[x],w[cnt]=z,hd[x]=cnt;} void dfs(int u,int fa) { for(int i=hd[u];i;i=nxt[i]) if(v[i]!=fa) { dfs(v[i],u); if(!w[i])g[u]+=g[v[i]]+1; else f[u]+=f[v[i]]+g[v[i]]+1; } } void dfs2(int u,int fa) { ans+=nf[u]+ng[u]; for(int i=hd[u];i;i=nxt[i]) if(v[i]!=fa) { if(!w[i])nf[v[i]]=f[v[i]],ng[v[i]]=ng[u]; else nf[v[i]]=nf[u]-g[v[i]]+ng[u],ng[v[i]]=g[v[i]]; dfs2(v[i],u); } } int main() { scanf("%d",&n); for(int i=1,x,y,z;i<n;i++)scanf("%d%d%d",&x,&y,&z),adde(x,y,z),adde(y,x,z); dfs(1,0); nf[1]=f[1],ng[1]=g[1],dfs2(1,0); cout<<ans; }
E
口胡了一个分治做法,一写发现,它居然过了。感觉本题比D简单。
做法大致如下:直接算很难处理,考虑分治,对于长度大于等于3的区间[l,r],考虑覆盖mid和mid+1的所有区间,可以把[l,r]分为[l,mid]和[mid+1,r]两半,然后mx[i]对于左半部分表示后缀最大值,对于右半部分表示前缀最大值,然后枚举位置计算另一端的位置是否符合题意,因为区间的max值出现在两端的mx之一,所以左右都搜一下即可统计所有答案。复杂度O(nlogn)
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=2e5+7; int n,ans,a[N],b[N],mx[N]; void solve(int l,int r) { if(l+1>=r)return; int mid=l+r>>1; solve(l,mid),solve(mid+1,r); mx[mid]=a[mid];for(int i=mid-1;i>=l;i--)mx[i]=max(mx[i+1],a[i]); mx[mid+1]=a[mid+1];for(int i=mid+2;i<=r;i++)mx[i]=max(mx[i-1],a[i]); for(int i=l;i<=mid;i++) { int pos=b[mx[i]-a[i]]; if(pos>mid&&pos<=r&&mx[pos]<mx[i])ans++; } for(int i=mid+1;i<=r;i++) { int pos=b[mx[i]-a[i]]; if(pos>=l&&pos<=mid&&mx[pos]<mx[i])ans++; } } int main() { scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&a[i]),b[a[i]]=i; solve(1,n); printf("%d",ans); }
F
很容易想到dp,令f[i][j]表示第i轮当前卡为j且游戏继续的概率,然后根据第i轮每张卡有1/(n-i+1)的概率选中,直接写个前缀和,根据题意暴力DP转移即可,代码20行。
感觉这题更简单,CF这场什么垃圾排题顺序,难怪Unrated
#include<bits/stdc++.h> using namespace std; const int N=5005,mod=998244353; int n,ans,a[N],s[N],inv[N],f[N][N]; int main() { scanf("%d",&n); for(int i=1,x;i<=n;i++)scanf("%d",&x),a[x]++; for(int i=n;i;i--)s[i]=s[i+1]+a[i]; inv[1]=1;for(int i=2;i<=n;i++)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod; for(int i=1;i<=n;i++)f[1][i]=1ll*a[i]*inv[n]%mod; for(int i=2;i<=n;i++) for(int j=1,sum=0;j<=n;j++) f[i][j]=1ll*a[j]*sum%mod,sum=(sum+1ll*f[i-1][j]*inv[n-i+1])%mod; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(a[j]>1)ans=(ans+1ll*f[i][j]%mod*(a[j]-1)%mod*inv[n-i])%mod; printf("%d",ans); }
G
看了下是个没有意思的大模拟,不想写也不会写,咕了。
感觉真实难度顺序:C<B<E<D=F<G<A