Codeforces Round #770 (Div.2) A-F 完整題解
A.Reverse and Concatenate
容易發現,對於一個迴文串,s+rev(s)=rev(s)+s;而任意字串在執行一次操作以後會變成迴文串,即ans=(是迴文串?)1:2。
注意特判k=0的情況。
#include <bits/stdc++.h> using namespace std; typedef long long ll; int main(){ ios_base::sync_with_stdio(false); cin.tie(0); int t;cin >> t; while (t--) { intn,a,b,c; cin>>a>>b; string s;cin>>s; string m=s;std::reverse(s.begin(), s.end()); if(b==0){cout<<1<<endl;continue;} if(s==m){cout<<1<<endl;continue;} else {cout<<2<<endl;continue;}
B. Fortune Telling
比較陰間的思維題
考慮到加法和xor產生的答案奇偶性一致,即可做出本題。
#include <bits/stdc++.h> using namespace std; typedef long long ll; ll x[200005]; int main(){ ios_base::sync_with_stdio(false); cin.tie(0); int t;cin >> t; while (t--) { ll n,a,b,c; cin>>n>>a>>b;b%=2;for(int i=1;i<=n;i++){cin>>x[i];x[i]%=2;b+=x[i];} b=abs(a-b)%2; if(b==0){cout<<"Alice"<<endl;} else cout<<"Bob"<<endl; } }
C. OKEA
題意:對於n*k個物品,第i個i元,重新擺放所有物品,使得對於每一行任意k個物品,其平均價值是整數。
首先,可以直觀的發現,對於一行的物品,要麼價值全為奇數,要麼價值全為偶數,否則存在一個奇數價值的物品與一個偶數價值的物品相鄰,平均數不是整數;因此,對於給定n、k,若其中奇數個數無法恰好被k整除,它一定無解,計算髮現這樣的情況是n%2==1&&k!=1;
其次,對於剩下的情況,考慮構造的方法,能夠很直觀地發現:使用等差數列的擺放方式可以使得解存在。
#include <bits/stdc++.h> using namespace std; typedef long long ll; int main(){ ios_base::sync_with_stdio(false); cin.tie(0); int t;cin >> t; while (t--) { int n,k; cin>>n>>k; if(n%2==1&&k!=1){cout<<"NO"<<endl;continue;} else{cout<<"YES"<<endl; if(n%2==1){ for(int i=1;i<=n;i++)cout<<i<<'\n'; } else{int cnt=1; for(int i=1;i<=n/2;i++){ for(int j=1;j<=k;j++){cout<<cnt<<' ';cnt+=2;} cout<<'\n'; } cnt=2; for(int i=1;i<=n/2;i++){ for(int j=1;j<=k;j++){cout<<cnt<<' ';cnt+=2;} cout<<'\n'; } } } } }
D. Finding Zero互動題
題意:有一組陣列,其中恰有一個0;可以選擇陣列中的三個數詢問,返回值為其中最大的數-最小的數;最多詢問2*n-2次,要求輸出兩個可能是0的位置。
題目暗示1:最後答案輸出的是兩個位置,換言之,你無法通過詢問恰好找到某個數字是0
題目暗示2:只有一個0,但是題目返回的不是數字,而是兩個數的差值。也就是說,考慮的是數字間的大小關係,因此把題目中的0理解做“最小的數”比較合適。
考慮任意四個數a,b,c,d,不妨設a<=b<=c<=d;
對這四個數中的每三個詢問一次,返回答案為d-a,d-b,c-a,d-a
驚喜地發現d-a出現了兩次,這兩次詢問分別為a c d與a b d;於是對於這樣的兩組詢問,可以分別排除b與c;
當c-a=d-a或者d-b=d-a時,通過驗算就能發現,上述性質不受到影響;從而每四次詢問必然能夠排除兩個數,我們從而可以在2n-2次詢問以內找到答案。
請注意考慮邊界情況的處理;筆者因為沒有考慮吃了五發WA。
#include <bits/stdc++.h> using namespace std; int m[4],ans[4]; int main(){ ios_base::sync_with_stdio(false); cin.tie(0); int t;cin >> t; while (t--) { int qry=0; int n;cin>>n;int mx; int bj[4]={0,0,0,0}; int cnt=5; m[0]=1,m[1]=2,m[2]=3,m[3]=4; while(qry<(n-1)/2*4){ mx=0; for(int i=0;i<4;i++){ cout<<"? "; for(int j=0;j<4;j++){if(i!=j)cout<<m[j]<<' ';} cout<<endl; cin>>ans[i]; if(ans[i]>mx)mx=ans[i]; } int ct=0; for(int i=0;i<4;i++){ if(ans[i]==mx&&ct<2){bj[i]=0,ct++;} else bj[i]=1; } for(int i=0;i<4;i++)if(bj[i]==0){ m[i]=cnt++; if(cnt>n){ cnt-=n; } for(int j=0;j<=3;j++){if(j==i)continue;if(m[j]==m[i])cnt++,m[i]++;} } qry+=4; } cout<<"! "; for(int i=0;i<4;i++){if(bj[i]==1)cout<<m[i]<<' ';} cout<<endl; } }View Code
E. Fair Share
題意:給出M個偶數長度的陣列,每個陣列中的數一半被放在多重集合L中,另一半被放在多重集合R中。
是否存在一種分法,使得集合L與集合R中的數完全一致?
一個顯然的必要條件是,每一種數必須在所有M個數組中出現偶數次,否則無解。
接下來,對於該條件的充分性,考慮構造法。由於題目要求把所有數置於兩個集合中,並且任意一個數在L/R的出現次數相等,任意一個數組中的數在L/R中的出現次數相等,這會使人想到二分圖染色的方法。於是題目的難點變成構造一個滿足題意的二分圖。
考慮對每個數出現的第1、3、5……i次和第2、4、6、……i+1次連邊,對每個陣列中的第1、3、5、……i個數和第2、4、6……i+1個數連邊,從而使得每個點的度均為2,可以形成一張二分圖。
然後填上L、R,然後AC。
#include <bits/stdc++.h> using namespace std; vector<vector<int>>mp,cnt; vector<vector<char>>color; map<int,int>lsh; vector<pair<int,int>>eg[200010]; char col[2]={'L','R'}; void dfs(int i,int j,int nmb){ if(color[i][j]!=0)return; color[i][j]=col[nmb]; if(color[i][j^1]!=0){ dfs(eg[lsh[mp[i][j]]][cnt[i][j]^1].first,eg[lsh[mp[i][j]]][cnt[i][j]^1].second,nmb^1); } else dfs(i,j^1,nmb^1); } int main() {int tot=0; int m;cin>>m; mp.resize(m);color.resize(m);cnt.resize(m); for(int i=0;i<m;i++){int n;cin>>n; mp[i].resize(n);color[i].resize(n);cnt[i].resize(n); for(int j=0;j<n;j++){ cin>>mp[i][j]; if(lsh[mp[i][j]]==0)lsh[mp[i][j]]=++tot; eg[lsh[mp[i][j]]].emplace_back(i,j); cnt[i][j]=eg[lsh[mp[i][j]]].size()-1; } } for(int i=1;i<=tot;i++){ if(eg[i].size()&1){cout<<"NO\n"; return 0;} } for(int i=0;i<m;i++){ for(int j=0;j<mp[i].size();j++){ dfs(i,j,0); } }cout<<"YES"<<'\n'; for(int i=0;i<m;i++){ for(int j=0;j<mp[i].size();j++){ cout<<color[i][j]; }cout<<'\n'; } }爛爛程式碼,別看QWQ
F. Fibonacci Additions
題意:對於一個區間[l,r],有一種叫作“斐波那契加”的操作,即對於[l,r]區間,x[l] += F[1],x[l+1] += F[2],……,x[r] += F[r-l+1];其中F為斐波那契數列,即F[1]=1,F[2]=1,F[3]=2……。
給定兩個長度為N的區間與模數p,對於q次操作,每次操作在A/B的一個區間上執行一次斐波那契加。每次加完後,若A、B模p意義下同餘,則輸出"YES",否則輸出"NO"。
對於這樣的題目,先考慮作一些對於複雜度沒什麼用,但是比較顯然,並且讓題目更加簡明的優化。
優化1:令陣列C[i]=(A[i]-B[i])%p,則當C全部是0時,輸出YES,否則輸出NO。
優化2:考慮維護cnt,即陣列C中非0元素的數量,cnt=0時有cnt全部為零。
優化3:預處理出斐波那契數列在模p意義下的每一項。
所有顯而易見的優化都用完了。
思考1:如果是普通的區間加,我們會考慮維護一個差分數列,這樣對於每一次區間和,我們只需要修改差分數列中的兩個數。
思考2:對於區間加,我們的差分思想來自於(xi+c) - (xi-1+c) = xi - xi-1。
那麼對於區間斐波那契加呢?
我們可以類似地發現:
(xi+Fi) - (xi-1-Fi-1) - (xi-2-Fi-2)=xi - xi-1 - xi-2
從而,我們維護一個斐波那契差分數列D,其中D[1]=C[1],D[i]=C[i]-C[i-1]-C[i-2];從而對於每一次斐波那契加,我們只需要考慮D[i]區間邊界上最多三個數值的變化,因此複雜度降至O(n);
同樣的,我們用cnt來維護這樣一列D。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=3e5+10; ll a[N],b[N],c[N],d[N]; ll f[N]; int main(){cin.tie(0);ios::sync_with_stdio(0);cout.tie(0); int n,q,mod,cnt=0; cin>>n>>q>>mod;f[1]=1; for(int i=2;i<=n;i++){ f[i]=(f[i-1]+f[i-2])%mod; } for(int i=1;i<=n;i++)cin>>a[i]; for(int i=1;i<=n;i++)cin>>b[i]; for(int i=1;i<=n;i++)c[i]=(a[i]-b[i]+mod)%mod; d[1]=c[1]; if(d[1]!=0)cnt++; for(int i=2;i<=n;i++){ d[i]=((c[i]-c[i-1]-c[i-2])%mod+mod)%mod; if(d[i]!=0)cnt++; } while(q--){ char ch;int l,r; cin>>ch>>l>>r; if(ch=='A'){ if(d[l]==0)cnt++; d[l]=(d[l]+1)%mod; if(d[l]==0)cnt--; if(d[r+1]==0&&r+1<=n)cnt++; d[r+1]=(d[r+1]+mod-f[r-l+2])%mod; if(d[r+1]==0&&r+1<=n)cnt--; if(d[r+2]==0&&r+2<=n)cnt++; d[r+2]=(d[r+2]+mod-f[r-l+1])%mod; if(d[r+2]==0&&r+2<=n)cnt--; } if(ch=='B'){ if(d[l]==0)cnt++; d[l]=(d[l]+mod-1)%mod; if(d[l]==0)cnt--; if(d[r+1]==0&&r+1<=n)cnt++; d[r+1]=(d[r+1]+f[r-l+2])%mod; if(d[r+1]==0&&r+1<=n)cnt--; if(d[r+2]==0&&r+2<=n)cnt++; d[r+2]=(f[r-l+1]+d[r+2])%mod; if(d[r+2]==0&&r+2<=n)cnt--; } if(cnt)cout<<"NO\n"; else cout<<"YES\n"; } }看到這裡,這道題已經不難了,相信你的程式碼會寫得比我更好。