ABC 373 E - How to Win the Election(二分答案)

题目传送门

解题思路

我们可以先二分答案每个人的最小的 x

然后对于每一个 x,我们对其判断是否能符合题意。

判断过程:

我们可以使用贪心思想,将被第 i 个人选取的 x 个选票除外的剩余选票,尽量分给原有投票数量大的人,看看加上这些剩余的选票之后,能不能有 m 个人大于第 i 个人的选票。

首先可以将每个人的初始票数 a_i 从小到大排个序;

找出第一个比 a_i+x 大的数的前一个位置 pos

那我们选取票数尽量大的(也就是 n-m+1 到 n);

那么我们要分情况讨论:

1. a_i 不在 [n-m+1,n] 中,

如果 pos<n-m+1,也就是已经有 m 个人票数比他大了,直接返回 false;

否则,我们可以知道 [n-m+1,pos] 这段中是比 a_i+x 要小的,那我们维护一个后缀和来求区间和,将 [n-m+1,pos] 的区间和加上剩余票数,与 (pos-(n-m))*(a_i+x) 比较即可。

2. a_i 在 [n-m+1,n] 中,

由于 a_i 在里面,所以多取一个,变成 [n-m,n]

过程跟上面差不多,但是算 [n-m,n] 区间和时要把 a_i 减掉就行。

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,k,s;
int a[200001],b[200001];
int sum[200001];
bool check(int x,int wz)
{
    int temp=a[wz]+x;
    int rest=k-s-x;
    int pos=upper_bound(b+1,b+n+1,temp)-b-1;

    if(a[wz]<b[n-m+1])
    {
        if(pos<n-m+1)return 0;
        if(sum[n-m+1]-sum[pos+1]+rest>=(pos-(n-m))*(temp+1))return 0;
        else return 1;
    }
    else{
        if(pos<n-m)return 0;
        if(sum[n-m]-sum[pos+1]+rest-a[wz]>=(pos-(n-m))*(temp+1))return 0;
        return 1;
    }
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    cin>>n>>m>>k;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        s+=a[i];
        b[i]=a[i];
    }
    if(n==1)
    {
        cout<<0;
        return 0;
    }
    if(n==m)
    {
        for(int i=1;i<=n;i++)
        {
            cout<<0<<" ";
        }
        return 0;
    }
    sort(b+1,b+n+1);
    sum[n]=b[n];
    for(int i=n-1;i>=1;i--)
    {
        sum[i]=sum[i+1]+b[i];
    }
    int l,r,mid,ans;
    for(int i=1;i<=n;i++)
    {
        l=0,r=k-s,ans=-1;
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(check(mid,i))
            {
                ans=mid;
                r=mid-1;
            }
            else
            {
                l=mid+1;
            }
        }
        cout<<ans<<" ";
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值