CF457C C. Elections

题目链接
大意:有n个人,每个人有两个参数 a , b a,b a,b,表示第 i t h i-th ith人投a,让他改变注意需要b的代价。问你现在要使得投0的人最多,最少代价是多少?
思路:我们枚举其他人被投的最大次数,然后把多的全部投0,剩下的不够的再加上。
用一个线段树维护一下前x小的和就行了。
注意要特判一下一开始就合法的情况。
细节见代码:

#include<bits/stdc++.h>

#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define LL long long
#define SZ(X) X.size()
#define pii pair<int,int>
#define ALL(X) X.begin(),X.end()

using namespace std;

LL gcd(LL a, LL b) {return b ? gcd(b, a % b) : a;}
LL lcm(LL a, LL b) {return a / gcd(a, b) * b;}
LL powmod(LL a, LL b, LL MOD) {LL ans = 1; while (b) {if (b % 2)ans = ans * a % MOD; a = a * a % MOD; b /= 2;} return ans;}
const int N = 2e5 + 11;
int n;
int a[N],b[N],c[N];
vector<int>v[N];
set<int>Q[N];
int T[N],t[N*40];
LL s[N*40];
#define mid (l+r>>1)
#define ls o<<1
#define rs o<<1|1
void update(int o,int l,int r,int d,int val){
    t[o]+=val;
    s[o]+=d*val;
    if(l==r)return ;
    if(d<=mid)update(ls,l,mid,d,val);
    else update(rs,mid+1,r,d,val);
}
LL get(int o,int l,int r,int k){
    //cout<<o<<' '<<l<<' '<<r<<' '<<k<<endl;
    if(t[o]<=k)return s[o];
    if(l==r)return 1ll*min(k,t[o])*l;
    if(t[ls]>=k)return get(ls,l,mid,k);
    else return get(rs,mid+1,r,k-t[ls])+s[ls];
}
int main() {
	ios::sync_with_stdio(false);
    cin>>n;
    int sta=0;
    for(int i=1;i<=n;i++){cin>>a[i]>>b[i],c[i]=a[i];if(a[i])update(1,0,200000,b[i],1);if(!a[i])sta=1;}
    if(!sta){a[++n]=0;b[n]=0;c[n]=0;}
    sort(c+1,c+1+n);
    int x=unique(c+1,c+1+n)-c-1;
    for(int i=1;i<=n;i++)a[i]=lower_bound(c+1,c+1+x,a[i])-c,v[a[i]].pb(b[i]);
    for(int i=2;i<=x;i++){
        sort(ALL(v[i]));
        reverse(ALL(v[i]));
        Q[SZ(v[i])].insert(i);
    }
    int cnt=SZ(v[1])-(!sta);
    LL ans=0;
    LL res=LLONG_MAX;
    if(cnt>(n-(!sta))/2+1)return cout<<0<<endl,0;
    for(int i=n;i>=0&&i>=cnt;i--){//枚举其他的最大人数是i 如果已经超过i了就break
        vector<int>re;
        for(auto k=Q[i+1].begin();k!=Q[i+1].end();k++){//把超过的部分删掉
            if(*k==1)continue;
            ans+=v[*k].back();
            update(1,0,200000,v[*k].back(),-1);
            v[*k].pop_back();
            cnt++;
            re.pb(*k);
        }
        for(auto k:re){
            Q[i+1].erase(k);
            Q[i].insert(k);
        }
        res=min(res,ans+get(1,0,200000,max(0,min(i+1,(n-(!sta))/2+1)-cnt)));
    }
    cout<<res<<endl;
	return 0;
}
全部评论

相关推荐

不愿透露姓名的神秘牛友
12-03 15:41
已编辑
淘天 算法工程师 31.0k*16.0
点赞 评论 收藏
分享
Natrium_:这时间我以为飞机票
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务