Goodbye 2020題解(A-G)
CF1466A:
列舉兩點,統計不同長度即可。
CF1466B:
從小到大,對於每一個數,如果它小於或等於上一個數,將其加$1$。如果大於等於上一個數則答案加1。
因為輸入已經排好序,所以正確性顯然。
程式碼:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int T,n,t,lst,ans; int main(void) { scanf("%d",&T); while(T--){ scanf("%d",&n); lstView Code=-1; ans=0; while(n--){ scanf("%d",&t); if(t==lst) t++; if(t>lst) ans++; if(lst<t) lst=t; } printf("%d\n",ans); } return 0; }
CF1466C:
等價於沒有長度為$2$或$3$的迴文串,即不存在$i$使$s_{i-1}=s_i$或者$s_{i-1}=s_{i+1}$。
所以如果有兩個相同的字元隔得足夠近,必須改其中至少一個。
又因為字符集夠大,所以由上述條件建成的圖(兩個必須改一個則1連邊)中最小點覆蓋就是答案。
因為只有相鄰點有連邊,所以考慮$dp$。
$dp_{0/1,0/1,i}$表示前$i$個字元,第$i-1$個字元不改/改,第$i$個字元不改/改的最小代價。
強行分類,然後轉移。
程式碼很噁心:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int T,n; char s[100005]; int dp[2][2][100005]; int main(void) { scanf(View Code"%d",&T); while(T--){ scanf(" %s",s); n=strlen(s); if(n==1){printf("0\n");continue;} dp[0][0][1]=0; dp[0][1][1]=1; dp[1][0][1]=1; dp[1][1][1]=2; if(s[0]==s[1]) dp[0][0][1]=1e9; for(int i=2;i<n;i++){ dp[0][1][i]=min(dp[0][0][i-1],dp[1][0][i-1])+1; dp[1][1][i]=min(dp[0][1][i-1],dp[1][1][i-1])+1; if(s[i]==s[i-1]&&s[i]==s[i-2]){ dp[0][0][i]=1e9; dp[1][0][i]=dp[1][1][i-1]; } if(s[i]==s[i-1]&&s[i]!=s[i-2]){ dp[0][0][i]=1e9; dp[1][0][i]=min(dp[1][1][i-1],dp[0][1][i-1]); } if(s[i]!=s[i-1]&&s[i]==s[i-2]) if(i>2&&s[i-1]==s[i-3]){ dp[0][0][i]=dp[1][1][i-2]; dp[1][0][i]=dp[1][1][i-1]; } else{ dp[0][0][i]=dp[1][0][i-1]; dp[1][0][i]=dp[1][1][i-1]; } if(s[i]!=s[i-1]&&s[i]!=s[i-2]){ dp[0][0][i]=min(dp[0][0][i-1],dp[1][0][i-1]); dp[1][0][i]=min(dp[0][1][i-1],dp[1][1][i-1]); } } int ans=min(dp[0][0][n-1],dp[0][1][n-1]); ans=min(ans,min(dp[1][0][n-1],dp[1][1][n-1])); printf("%d\n",ans); } return 0; }
CF1466D:
每一種顏色只取最大權值和的連通塊,所以把其它連通塊換顏色答案不會更劣。
於是每種顏色只有一個連通塊。
假設有$k$種顏色,那麼這$k$個連通塊共有$k-1$個交點。
總權值就是所有點權乘加上該點被包含次數之和。
所有點被包含次數和明顯為$n+k-1$(每加入一種顏色次數和加一),每個點最多被包含的次數是其度數。
於是把點權放在一起排序並求字首和即可。
程式碼很簡單:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define ll long long int int T,n,a,b; ll ans; int deg[100000],w[100000]; int val[200000],cnt; int main(void) { scanf("%d",&T); while(T--){ scanf("%d",&n); cnt=0; ans=0; for(int i=0;i<n;i++){ scanf("%d",&w[i]); deg[i]=0; ans+=w[i]; } for(int i=1;i<n;i++){ scanf("%d%d",&a,&b); a--; b--; if(deg[a]>0) val[cnt++]=w[a]; if(deg[b]>0) val[cnt++]=w[b]; deg[a]++; deg[b]++; } sort(val,val+cnt); for(int i=cnt-1;i>=0;i--){ printf("%lld ",ans); ans+=val[i]; } printf("%lld\n",ans); } return 0; }View Code
CF1466E:
$$\sum_i^n \sum_j^n \sum_k^n(x_i \& x_j)(x_j \| x_k)$$
$$= \sum_j^n ( \sum_i^n x_i \& x_j)( \sum_k^n x_j \| x_k)$$
對於每一位,計算在全部$x_i$種這一位$1$的個數即可計算左右兩邊的值。
程式碼:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define ll long long int #define mod 1000000007 inline int sum(int a,int b){return a+b<mod?a+b:a+b-mod;} inline int mul(int a,int b){return (int)((ll)a*b%mod);} int T,n; ll a[500000]; int p1[500000],p2[500000]; int main(void) { scanf("%d",&T); while(T--){ scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%lld",&a[i]); p1[i]=p2[i]=0; } int w=1; for(int i=0;i<60;i++){ int f0=0,f1=0; for(int j=0;j<n;j++) if((a[j]>>(ll)i)&1ll) f1++; else f0++; for(int j=0;j<n;j++) if((a[j]>>(ll)i)&1ll){ p1[j]=sum(p1[j],mul(w,f1)); p2[j]=sum(p2[j],mul(w,n)); } else p2[j]=sum(p2[j],mul(w,f1)); w=sum(w,w); } int ans=0; for(int i=0;i<n;i++) ans=sum(ans,mul(p1[i],p2[i])); printf("%d\n",ans); } return 0; }View Code
CF1466F:
兩個位置是$1$則在這兩個位置連邊,一個位置打標記,這樣一個連通塊中:
如果有標記則這些位置中的任意狀態均可以達到。
如果沒有標記則在這些位置中,$1$的個數為偶數的狀態都可以達到。
證明?有標記的話所有涉及兩個位置的操作都可以轉化成一個位置的操作。
無標記就找生成樹,一個狀態就是一些需要改變的點,於是從葉子到根依次執行該點和其父親的邊所對應操作即可。
並查集維護連通塊可以找最小字典序的解。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define ll long long int #define mod 1000000007 inline int mul(int a,int b){return (int)((ll)a*b%mod);} int n,m,ans=1; int fa[500000]; bool have[500000]; int use[500000],cnt=0; int find(int x) { if(x==fa[x]) return x; fa[x]=find(fa[x]); return fa[x]; } int main(void) { scanf("%d%d",&n,&m); for(int i=0;i<m;i++) fa[i]=i; for(int i=1;i<=n;i++){ int k,a,b; scanf("%d%d",&k,&a); if(k==2) scanf("%d",&b); a--; b--; if(k==1){ a=find(a); if(!have[a]) use[cnt++]=i; if(!have[a]) ans=mul(ans,2); have[a]=1; } if(k==2){ a=find(a); b=find(b); if(a==b||(have[a]&&have[b])) continue; fa[a]=b; have[b]|=have[a]; use[cnt++]=i; ans=mul(ans,2); } } printf("%d %d\n%d",ans,cnt,use[0]); for(int i=1;i<cnt;i++) printf(" %d",use[i]); putchar('\n'); return 0; }View Code
CF1466G:
將$s_i$變成$s_{i+1}=s_it_is_i$看作一次展開操作。
對於單個查詢:
暴力展開原字串直到長度大於查詢串長度。
分兩種情況:
第一種是展開後字串的字串,這部分未來每次展開操作後出現次數翻倍。
剩下的下午寫。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define ll long long int #define mod 1000000007 inline int sum(int a,int b){return a+b<mod?a+b:a+b-mod;} inline int mul(int a,int b){return (int)((ll)a*b%mod);} inline int pow(int a,int b) { int res=1; while(b>0){ if(b&1) res=mul(res,a); a=mul(a,a); b>>=1; } return res; } inline int inv(int x){return pow(x,mod-2);} #define mod2 998244353 inline int sum2(int a,int b){return a+b<mod2?a+b:a+b-mod2;} inline int mul2(int a,int b){return (int)((ll)a*b%mod2);} const int base=29; int n,m=0,M=0,q; char s[4000005]; char trans[100005]; int L[1000],R[1000]; int hsh1[4000005]; int hsh2[4000005]; int pow1[2000005]; int pow2[2000005]; char qu[1000005]; int hshq1[1000005]; int hshq2[1000005]; int ql[100000]; int qr[100000]; int qk[100000]; int sumv[26][100005]; int pow02[100005]; int ipow2[100005]; int res[26]; void read(void) { scanf("%d%d",&n,&q); scanf(" %s %s",s,trans); L[0]=0; R[0]=strlen(s)-1; int lst=-1; for(int i=0;i<q;i++){ ql[i]=lst+1; scanf("%d %s",&qk[i],qu+ql[i]); qr[i]=lst=ql[i]+strlen(qu+ql[i])-1; M=max(M,qr[i]-ql[i]+1); } while(m<n&&R[m]-L[m]+1<M){ int len=R[m]-L[m]+1; m++; L[m]=R[m-1]+1; R[m]=L[m]+len+len; s[L[m]+len]=trans[m-1]; for(int i=0;i<len;i++) s[L[m]+i]=s[L[m]+len+1+i]=s[L[m-1]+i]; } return; } void gethsh(void) { pow1[0]=pow2[0]=1; for(int i=1;i<=M+M;i++){ pow1[i]=mul(pow1[i-1],base); pow2[i]=mul2(pow2[i-1],base); } for(int i=0;i<=m;i++){ hsh1[L[i]]=hsh2[L[i]]=s[L[i]]-'a'; for(int j=L[i]+1;j<=R[i];j++){ hsh1[j]=sum(mul(hsh1[j-1],base),s[j]-'a'); hsh2[j]=sum2(mul2(hsh2[j-1],base),s[j]-'a'); } } for(int i=0;i<q;i++){ hshq1[ql[i]]=hshq2[ql[i]]=qu[ql[i]]-'a'; for(int j=ql[i]+1;j<=qr[i];j++){ hshq1[j]=sum(mul(hshq1[j-1],base),qu[j]-'a'); hshq2[j]=sum2(mul2(hshq2[j-1],base),qu[j]-'a'); } } return; } inline bool check(int i,int l1,int r1,int j,int l2,int r2) { if(l1>r1) return 1; int h1=hsh1[r1],h2=hshq1[r2],len=r1-l1+1; if(L[i]!=l1) h1=sum(h1,mod-mul(hsh1[l1-1],pow1[len])); if(ql[j]!=l2) h2=sum(h2,mod-mul(hshq1[l2-1],pow1[len])); if(h1!=h2) return 0; h1=hsh2[r1]; h2=hshq2[r2]; if(L[i]!=l1) h1=sum2(h1,mod2-mul2(hsh2[l1-1],pow2[len])); if(ql[j]!=l2) h2=sum2(h2,mod2-mul2(hshq2[l2-1],pow2[len])); if(h1!=h2) return 0; return 1; } int main(void) { read(); gethsh(); pow02[0]=ipow2[0]=1; for(int i=1;i<=n;i++){ pow02[i]=sum(pow02[i-1],pow02[i-1]); ipow2[i]=inv(pow02[i]); } for(int i=n-1;i>=0;i--){ for(int j=0;j<26;j++) sumv[j][i]=sumv[j][i+1]; int c=trans[i]-'a'; sumv[c][i]=sum(sumv[c][i],pow02[n-i]); } for(int i=0;i<q;i++){ int pos=0; while(pos<n&&R[pos]-L[pos]<qr[i]-ql[i]) pos++; if(pos>qk[i]){printf("0\n");continue;} int ans=0; for(int j=L[pos];j+qr[i]-ql[i]<=R[pos];j++) if(check(pos,j,j+qr[i]-ql[i],i,ql[i],qr[i])) ans=sum(ans,pow02[qk[i]-pos]); for(int j=ql[i];j<=qr[i];j++){ bool f1=check(pos,R[pos]+ql[i]-j+1,R[pos],i,ql[i],j-1); bool f2=check(pos,L[pos],L[pos]+qr[i]-j-1,i,j+1,qr[i]); if(f1&&f2) res[qu[j]-'a']++; } for(int c=0;c<26;c++){ res[c]=mul(res[c],mod+sumv[c][pos]-sumv[c][qk[i]]); ans=sum(ans,mul(res[c],ipow2[n-qk[i]+1])); res[c]=0; } printf("%d\n",ans); } return 0; }View Code
CF1466H:
不會
CF1466I:
不會