1. 程式人生 > 其它 >Codeforces Round #770 (Div.2) A-F 完整題解

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--) {
        int
n,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) = x- xi-1。

那麼對於區間斐波那契加呢?

我們可以類似地發現:

(xi+Fi) - (xi-1-Fi-1) - (xi-2-Fi-2)=x- 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";
    }
}
看到這裡,這道題已經不難了,相信你的程式碼會寫得比我更好。