CodeTonRound8-BC1C2補題

osir發表於2024-03-31

B-Bessie and MEX

思路:順,逆填都可以.見程式碼註釋

void solve(){           //補B--不用str.find來維護,這個是o(n)的。用set的count() or find()來維護,這兩個都是o(logn)的
    int n;cin>>n;
//    // 順著填:填的數字=MEX.find('0')-ai or 填了MEX[MEX.find('0')]='1',之後MEX.find('0')-剛才填的位置--這樣雖然是對的,但是實現容易超時
//    //順著填:x>=0,那麼pi即為當前mex;如果x<0,那麼pi=mex-x;
//    // 因為x>=0,即NewMex-CurMex>=0,那麼mex一定是增加的,那pi只能填當前mex;
//    // 如果x<0,那麼NewMex不能增加,那隻能保持不變,那pi只能是mex-x;
    set<int> st;
    int mex=0;
    for(int i=1;i<=n;i++){
        int x; cin>>x;
        while(st.find(mex)!=st.end()) mex++;
        if(x>=0){
            cout<<mex<<" ";
            st.insert(mex);
        }
        else{
            cout<<mex-x<<" ";
            st.insert(mex-x);
        }
    }
    cout<<endl;
}
void solve(){           //補B
    //倒著填:因為填了最後一個之後,mex必然為n,那麼最後一個pi就是n-arr[n];那麼填n-1個時,因為只有pn還沒出現過,那麼此時mex為pn
    //但是遇到負數時填的數字會更大,mex不會改變,那麼只有當mex-ai更小才更新mex
    //我們知道最後一個位置的MEX是n,那麼n−pn=an,我們可以得出 pn=n−an
    //現在知道了pn,那麼可以確定前n-1個數字的MEX.跟求出pn是一樣的.一直這樣計算下去即可.
    int n; cin>>n;
    int arr[200005];
    for(int i=1;i<=n;i++) cin>>arr[i];
    int cur=n-arr[n];
    stack<int> stk;
    stk.emplace(cur);
    for(int i=n-1;i>=1;i--){
        stk.emplace(cur-arr[i]);
        cur=min(cur,cur-arr[i]);
    }
    while(stk.size()){
        cout<<stk.top()<<" ";
        stk.pop();
    }
    cout<<endl;
}

C1-Bessie's Birthday Cake (Easy Version)

思路:見程式碼註釋.

void solve(){           //補C1    新
    int n,x,y; cin>>n>>x>>y;
    int ans=x-2;            //!!顯然!!任意x個點內部可以構成x-2個三角形.對於外部點三角形,也是顯然,只有兩個點距離恰好為2點時候可以構成外部一個三角形
    int arr[200005];
    for(int i=1;i<=x;i++) cin>>arr[i];
    sort(arr+1,arr+x+1);
    for(int i=2;i<=x;i++){
        if(arr[i]-arr[i-1]==2) ans++;
    }
    if(arr[1]+n-arr[x]==2) ans++;  //特判一下環的最後一個和第一個
    cout<<ans<<endl;
}

C2-Bessie's Birthday Cake (Hard Version)

思路:見程式碼註釋

void solve(){           //補C2   !貪心! 重重貪心,先是貪偶數,再是貪從小的先拿
    int n,x,y; cin>>n>>x>>y;
    int ans=x-2;
    int arr[200005];
    for(int i=1;i<=x;i++) cin>>arr[i];
    sort(arr+1,arr+x+1);
    arr[x+1]=arr[1]+n;
    //priority_queue<int> pq;      //不能單調從大到小排。因為偶的是比奇的更有價值的,所以應該優先考慮偶的區間,還有y剩餘的話,再考慮奇的區間(貪心)
    priority_queue<int,vector<int>,greater<int>> pqodd,pqeven;   //小根堆,優先拿小的
    for(int i=1;i<=x;i++){
        if(arr[i+1]-arr[i]==2) ans++;
        if(arr[i+1]-arr[i]>2&&(arr[i+1]-arr[i])%2==0) pqeven.emplace(arr[i+1]-arr[i]);
        if(arr[i+1]-arr[i]>2&&(arr[i+1]-arr[i])%2!=0) pqodd.emplace(arr[i+1]-arr[i]);
    }
    while(pqeven.size()&&y){
        //先貪心地跑偶的,因為得到相同的三角形個數,偶的可以花費更少y.
        // 先拿小的,是因為小的更值當;如果先拿大的,可能因為y不夠,導致貪心沒有貪到
        int cur=pqeven.top();
        pqeven.pop();
        if(cur%2==0&&y>=cur/2-1){
            y-=cur/2-1,ans+=cur/2-1,ans+=cur/2;
        }
        else if(cur%2==0&&y<cur/2-1){
            ans+=y,ans+=y,y=0;
        }
        else if(cur%2!=0&&y>=cur/2){
            y-=cur/2,ans+=cur/2,ans+=cur/2;
        }
        else if(cur%2!=0&&y<cur/2){
            ans+=y,ans+=y,y=0;
        }
    }
    while(pqodd.size()&&y){
        int cur=pqodd.top();
        pqodd.pop();
        if(cur%2==0&&y>=cur/2-1){
            y-=cur/2-1,ans+=cur/2-1,ans+=cur/2;
        }
        else if(cur%2==0&&y<cur/2-1){
            ans+=y,ans+=y,y=0;
        }
        else if(cur%2!=0&&y>=cur/2){
            y-=cur/2,ans+=cur/2,ans+=cur/2;
        }
        else if(cur%2!=0&&y<cur/2){
            ans+=y,ans+=y,y=0;
        }
    }
    cout<<ans<<endl;
}

總結:這場只寫出來AB題,B題因為用str.find()來維護,被hack了。所以只有A題,一場扣了100多分..再接再厲吧。