AtCoder Grand Contest 041 B.Voting Judges (二分)

题目链接:https://atcoder.jp/contests/agc041/tasks/agc041_b

题目大意:

有n个问题,m个裁判,每个裁判会给v个不同的问题投票,投一票加一分;同时每个问题还有一个ai作为初始分数,现在在所有裁判投票完成后,选取前p大的问题,如果a[p]==a[p+1]==a[p+2]这样的话,相同的分数都会选择进来,问你有多少个问题可能被选择?

思路:

比赛时二分写丑了,改了一下就过了。。。

首先肯定按照初始分数排序,这里按照从大到小排,每个问题最多被加m分(m个裁判都选择了这个问题),且最多会有v个问题被加m分。

如果a[i]这个问题可以,那么分数>a[i]的肯定也可以,所以我们考虑二分答案mid;

如何判断mid这个位置可不可以被选呢?

  • 如果mid<=p,那么一定是可以的。
  • 如果a[mid]+m
  • 剩下的情况,如果可以被选,那么我最优的选择方法肯定是a[1],a[2],a[3]........a[p-1],a[mid]。换句话说,我只需要保证a[mid]在所有加分完成之后比a[p]大就可以了。

现在考虑如何加分是最优的,我们一共有m*v分,首先将m分加给a[mid],然后有如下策略:

  1. 如果我将m分加给a[1],a[2]....a[p-1],并不会影响我与a[p]比较,这样做可以舍去(p-1)*v分。
  2. 如果我将m分加给a[mid+1],a[mid+2].....a[n],也不会有影响,因为不管怎么加,a[mid]+m>=a[mid+1]+m;这样做可以舍去(n-mid)*v分
  3. 剩下的分,我们平摊给p<=i

这样加完分之后,如果分数还有剩余,那说明一定有一个在p之后的元素会加分加到大于a[mid]+m,这种情景不行;否则,mid就是合法的。

#include 
using namespace std;
#define int long long
const int maxn=1e5+10;
int a[maxn];
int cmp(const int a,const int b){
    return a>b;
}
int n,m,v,p;
int b[maxn];
bool judge(int mid){
    //printf("hello %lld\n",mid);
    if(mid<=p)return true;
    if(a[mid]+m=a[p])return true;
    for(int i=1;i<=n;i++)b[i]=a[i];
    int temp=0;
    for(int i=1;i<=p-1;i++){
        b[i]+=m;
        temp+=m;
    }
    //给开头的p-1个元素加值
    int inx=n,cnt=v-p+1;
    //给后面的元素加值
    while(inx>mid&&cnt>1){
        temp+=m;
        inx--;
        cnt--;
        b[inx]+=m;
    }
    temp+=m;
    int now=a[mid]+m;
    int res=m*v-temp;
    //中间还能被填多少
    for(int i=p;ires)add=res;
        res-=add;
        b[i]+=add;
    }
    return now>=b[p]&&res<=0;
}
signed main(){
    cin>>n>>m>>v>>p;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    sort(a+1,a+n+1,cmp);
    int low=1,high=n;
    int ans=0;
    while(low<=high){
        int mid=(low+high)>>1;
        if(judge(mid)){
            low=mid+1;
            ans=mid;
        }
        else{
            high=mid-1;
        }
    }
    cout<

 

你可能感兴趣的:(二分,思维题)