CF1070F Debate

原题

Solution

考虑贪心。

那么 \(s_i=11\) 的肯定都选,因为这样不会使答案变劣

重点就是 \(10,01,00\) 之间怎么选

首先, \(00\) 肯定是最劣的,所以先考虑 \(01,10\)

有一个结论是:在最优情况, \(s_i=01\) 全部选出,或者 \(s_i=10\) 全部选出

证明:设现在选了 \(x\)\(01\) 的, \(y\)\(10\) 的,那么再选 \(\min(x,y)\)\(01\)\(10\) 显然是不会超过限制的。

按照 \(s_i=10\) 全部选择,然后考虑剩下的 \(01,00\)

因为要最大,即贪心。所以要按照 \(a_i\) 排序,然后枚举 \(s_i=01\) 的选择个数 \(j\)

\(num_{11}+j<num_{10}\) 即支持Bob的人太少,或 \(num_{11}+num_{10}<j\) 即支持Alice的人太少,就跳过这个 \(j\)

那么剩下的可选的就只有 \(s_i=00\) 的,数量为 \(\min\{num_{00},num_{11}+j-num_{10},num_{11}+num_{10}-j\}\)

其中第一个是 \(s_i=00\) 初始数量,第二个是最多还有多少人能不支持Bob,第三个是最多还有多少人能不支持Alice

\(s_i=01\) 同理即可

而枚举时候的价值可以拿前缀和优化,达到 \(O(n\log n)\) 的时间复杂度。

Code

#include<bits/stdc++.h>

using namespace std;
const int N=4e5+10;
int n,num[5],a[5][N],cnt,sum,ans;

inline bool cmp(const int &a,const int &b){
    return a>b;
}

int main(){
    scanf("%d",&n);
    for(int i=1,x,y;i<=n;i++){
        scanf("%d%d",&x,&y);
        if(!x) a[0][++num[0]]=y;
        else if(x==1) a[1][++num[1]]=y;
        else if(x==10) a[2][++num[2]]=y;
        else a[3][++num[3]]=y,sum+=y;
    }
    sort(a[0]+1,a[0]+num[0]+1,cmp);
    sort(a[1]+1,a[1]+num[1]+1,cmp);
    sort(a[2]+1,a[2]+num[2]+1,cmp);
    for(int i=2;i<=num[0];i++) a[0][i]+=a[0][i-1];
    for(int i=2;i<=num[1];i++) a[1][i]+=a[1][i-1];
    for(int i=2;i<=num[2];i++) a[2][i]+=a[2][i-1];
    for(int i=0;i<=num[1];i++){
        if(num[3]+i<num[2]||num[3]+num[2]<i) continue;
        ans=max(ans,a[1][i]+a[2][num[2]]+a[0][min(num[0],min(num[3]+i-num[2],num[3]+num[2]-i))]);
    }
    for(int i=0;i<=num[2];i++){
        if(num[3]+i<num[1]||num[3]+num[1]<i) continue;
        ans=max(ans,a[2][i]+a[1][num[1]]+a[0][min(num[0],min(num[3]+i-num[1],num[3]+num[1]-i))]);
    }
    printf("%d\n",ans+sum);
    return 0;
}

我这里把 \(00,01,10,11\) 看作二进制了,心领神会吧(o゚v゚)ノ

posted @ 2020-10-25 10:57  jasony_sam  阅读(65)  评论(0编辑  收藏  举报