cfRounddiv3--CDEF題解

osir發表於2024-05-12

C-Assembly via Remainders

思路:因為xi最大隻有500,而構造的ai最大可以到1e9,直接從501開始構造即可。

void solve(){           //C  簡單構造
    int n; cin>>n;
    vector<int> vct;
    vct.emplace_back(501);
    for(int i=2;i<=n;i++){
        int x; cin>>x;
        vct.emplace_back(vct[i-2]+x);
    }
    for(int i=0;i<n;i++) cout<<vct[i]<<" ";
    cout<<endl;
}

D-Permutation Game

思路:按著順序列舉即可。列舉每一個位置,可以計算出此後一直拿這個位置可以得到的最終的值。取max即可.計算雙方的最大可能得分來回答這個問題。

int p[200005];
int arr[200005];
void solve(){               //D         列舉n,貪心
    int n,k,sa,sb; cin>>n>>k>>sa>>sb;
    for(int i=1;i<=n;i++) cin>>p[i];
    for(int i=1;i<=n;i++) cin>>arr[i];
//    vector<int> pa,pb;
//    pa.emplace_back(sa);
//    pb.emplace_back(sb);
//    for(int i=2;i<=n;i++) pa.emplace_back(p[pa[i-2]]);
//    for(int i=2;i<=n;i++) pb.emplace_back(p[pb[i-2]]);
    int maxa=INT_MIN,suma=0,maxb=INT_MIN,sumb=0;
    for(int i=1;i<=k&&i<=n;i++){
        suma+=arr[sa];
        maxa=max(maxa,suma+(k-i)*arr[sa]);
        sa=p[sa];
        sumb+=arr[sb];
        maxb=max(maxb,sumb+(k-i)*arr[sb]);
        sb=p[sb];
    }
    if(maxa>maxb) cout<<"Bodya"<<endl;
    else if(maxb>maxa) cout<<"Sasha"<<endl;
    else cout<<"Draw"<<endl;
}

E-Cells Arrangement

思路:集合最大的值為(n-1+n-1).選了(1,1)和(1,2)可以貢獻0,1;兩個值.再選(n,n)可以貢獻(n-1+n-1)和(n-1+n-1-1). 再選(n-1,n-1)可以貢獻(n-1-1+n-1-1)和(n-1-1+n-1-1-1)都是會貢獻兩個不重複的,沒出現過的值。

舉例:n=4;

選擇(1,1)和(1,2)-->S={0,1};

選擇(4,4)-->S={0,1,6,5};

再選擇(3,3)-->S={0,1,6,5,4,3}; 呃呃這個應該是S={0,1,6,5,4,3,2}這樣選就產生了所有可能的距離。。

可是為什麼不會存在一個點,選了這個點可以貢獻3個沒出現過的值的呢?原因如上...呃呃歪打誤撞寫對了正解。。

主對角線到(1,1)產生偶數距離,到(1,2)產生奇數距離.

void solve(){               //E   巧妙構造題,如果想歪了的話,將會一直卡死  30分鐘
    int n; cin>>n;
    cout<<"1 1"<<endl;
    cout<<"1 2"<<endl;
    for(int i=n;i>=3;i--) cout<<i<<" "<<i<<endl;
    cout<<endl;
}

F-Equal XOR Segments

思路:不錯的題!

第一步:首先需要 發現 最終區分的區間都可以是兩段或三段。

因為大於三段的區間,都可以透過x^x^x=x化簡為兩段或三段。

發現上面性質之後,就可以進行下一步了。

第二步:

////F-Equal XOR Segments-相等的 XOR 段--好題
////手寫二分
int n,q;
int pre[200005];            ////經典的異或字首和
void solve(){               ////F   o(qlogn)  異或+二分(不好想到)
    cin>>n>>q;
    ////關鍵就在於此。mp是用作二分的容器。 first為字首異或的值--second為這些相同的字首異或值出現的下標..(需要對這些下標進行二分)
    map<int,vector<int>> mp;        ////TLE30..cf不要再用unordered_map了。。會專門被卡。。
    for(int i=1;i<=n;i++){
        cin>>pre[i];
        pre[i]^=pre[i-1];
        mp[pre[i]].emplace_back(i);    ////vector中存的是下標
    }
    while(q--){
        int l,r; cin>>l>>r;
        bool ans=false;
        ////先檢查能不能分為兩段
        if(pre[l-1]==pre[r]) ans=true;
        ////再檢查能不能分為三段
        if(!ans){
            int num1=pre[l-1],num2=pre[r];
            int minidxa=INT_MAX,maxidxb=INT_MIN;
//            vector va=mp[num1],vb=mp[num2];       //這個操作是時間複雜度是o(n)的,空間複雜度也是o(n)的
            int ll=0,rr=(int)mp[num2].size()-1;      ////注意這裡的ll和rr是 二分的是下標 --- 二分下標找在區間l,r中的下標..
            while(ll<=rr){
                int mid=(ll+rr)>>1;
                if(l<=mp[num2][mid]&&mp[num2][mid]<r){
                    minidxa=mp[num2][mid];
                    rr=mid-1;
                }
                else if(mp[num2][mid]>=r) rr=mid-1;
                else if(mp[num2][mid]<=l) ll=mid+1;
            }
            ll=0,rr=(int)mp[num1].size()-1;      ////超乎想象。。mp.size()不加強制型別轉換,在cf上跑會導致RE,找了很久都找不到錯。。原來這都會導致RE。
            while(ll<=rr){
                int mid=(ll+rr)>>1;
                if(l<mp[num1][mid]&&mp[num1][mid]<r){
                    ////右邊可以不取等號,左邊需要取等號。按定義的話,左邊取等就是第一個單獨一個區間,右邊取等沒意義,越界了。第二個斷點取r-1的時候,r就是單獨一個區間的含義了。
                    maxidxb=mp[num1][mid];
                    ll=mid+1;
                }
                else if(mp[num1][mid]>=r) rr=mid-1;
                else if(mp[num1][mid]<=l) ll=mid+1;
            }
            if( l<=minidxa && minidxa<maxidxb && maxidxb<r ) ans=true;
        }
        if(ans) cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
    }
    cout<<endl;
}