#51830: 最簡單的題解(前綴和+後綴和+二分搜)


yoyokuo1129@gmail.com (A u)

學校 : 不指定學校
編號 : 248490
來源 : [203.64.138.253]
最後登入時間 :
2025-04-25 15:52:01

#include<bits/stdc++.h>
using namespace std;
int ans=0;
int n,k;
int mxn=300005;

vector<int> a(mxn+2);
vector<int> pre(mxn+2); //前綴奇偶和 (偶數-1 奇數+1)
vector<int> bac(mxn+2); //後綴奇偶和 (偶數-1 奇數+1)
vector<int> su(mxn+2);  //前綴數字和

//cnt[x]裡存放前綴奇偶和或後綴奇偶和的值為x的索引
//並且都先預設填入一個最大值(排序後才會在最後),因為待會的二分搜會跳過最後一個索引
vector<vector<int>> cnt(600005,vector<int>(1,INT_MAX));

bool can(int i,int num){
    if(i>=num){return false;}
    if(su[i] + su[n]-su[num-1]<=k){
        ans=max(ans,su[i] + su[n]-su[num-1]);
        return true;
    }
    return false;
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    cin>>n>>k;
    pre[0]=0;
    bac[n+1]=0;
    for(int i=1;i<=n;i++){cin>>a[i];}

    for(int i=1;i<=n;i++){
        pre[i] = (a[i]%2)==0? pre[i-1]-1:pre[i-1]+1;
        su[i] = su[i-1]+a[i];
        int j=n-i+1;
        bac[j] = (a[j]%2)==0? bac[j+1]-1:bac[j+1]+1;
        cnt[bac[j]+300000].push_back(j);
    }

    //最右側後綴奇偶和都不選也為0
    cnt[0+300000].push_back(n+1);

    //在主程式外先預先排序
    for (int w=0; w<600005; w++) {
        if (!cnt[w].empty()){
            sort(cnt[w].begin(),cnt[w].end());
        }
    }

    //二分搜
    for(int l=0;l<=n;l++){
        int w=-pre[l]+300000;
        int r=cnt[w].size()-1;
        int jump=cnt[w].size()/2;
        while(jump>0){
            while(r-jump >= 0 && can(l,cnt[w][r-jump])){
                r-=jump;
            }
            jump/=2;
        }

    }
    cout<<ans;
    return 0;
}