Codeforces Contest 457 C Elections —— 三分

This way

题意:

有n个人要投票,第i个人会投给第ai个人。你是第0个人,你需要收买一些人使得最终你的票数比任何人的票数都多。第i个人需要bi块钱去收买。

题解:

在cf上不能交的题目,拉到vjudge上就能交了,非常神奇。
一开始以为是二分最终有多少票,因为那时候觉得票数越少买的人就越少,那么花的钱就越少。但是不是这样的,有时候买的人数多一点范围就会更广一点,举个例子:
13
1 10000
1 10000
1 10000
2 10000
2 10000
2 10000
3 10000
3 10000
3 10000
4 1
5 1
6 1
7 1
当我二分到3的时候,我肯定要把1,2,3那里分别买最少的,需要花30000元,由于是合法的,因为3的时候必须买的最少人数<=3,什么叫最少买的人数,假设我二分到2,那么所有人数>=2的人我都需要使得他们<2,那么我就需要买6张票,但是6>=2,所以买两张票就胜利是不可能的。把所有人降低到你的人数以下的最少收买人数就叫最少买的人数。
显然二分是错的,那么怎么办,我们发现他有一个显然的性质,当你买的人数大到一定程度的时候,继续买一定花的钱更多。那么这就是一个类似 y = − x 2 y=-x^2 y=x2的曲线,那么就能用三分。
在三分的时候首先我们看看买这么多人是否可能,可能的话我们就做,我们用两种multiset记录信息:第一种是1e5个人,每个人那里收买的所有可能性,第二个是一个pair存的multiset,代表所有人投的票的信息,存的第一个数是收买这个人需要花的钱,第二个数是这个人投给了谁。
首先我们将所有人数>=x的人那里买,之后如果不够就从所有的人那里买,用vis记录已经买过的信息。
注意三分的退出条件是r>l+2,并且三分的话可能不会分到边界值的情况,所以最后要坐一下l和r的情况。

#include<bits/stdc++.h>
using namespace std;
#define pa pair<int,int>
const int N=1e5+5;
multiset<int>st[N];
struct node
{
    int id,num;
    bool operator< (const node& a)const
    {
        return num>a.num;
    }
}p[N];
multiset<pa>rt;
int zero;
map<pa,int>vis;
int judge(int x)
{
    int pos=1,cnt=zero;
    while(p[pos].num>=x&&p[pos].num>0)
        cnt+=p[pos++].num-x+1;
    return cnt<=x;
}
int check(int x)
{
    vis.clear();
    int pos=1,cnt=zero,money=0;
    while(p[pos].num>=x&&p[pos].num>0)
    {
        int res=p[pos].num-x+1;
        for(multiset<int>::iterator it=st[p[pos].id].begin();res>0;res--,it++)
        {
            money+=*it;
            cnt++;
            vis[{*it,p[pos].id}]++;
        }
        pos++;
    }
    for(multiset<pa>::iterator it=rt.begin();cnt<x;it++)
    {
        if(vis[*it]>0)
        {
            vis[*it]--;
            continue;
        }
        money+=(*it).first;
        cnt++;
    }
    return money;
}
int main()
{
    int n,x,y;
    scanf("%d",&n);
    for(int i=1;i<N;i++)
        p[i].id=i;
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&x,&y);
        if(x!=0)
            rt.insert({y,x});
        st[x].insert(y);
        p[x].num++;
        if(x==0)
            zero++;
    }
    sort(p+1,p+N-1);
    int low=zero,high=n,lm,rm,ans=low;
    int m1=1e9,m2=1e9;
    while(high>low+2)
    {
        lm=(low*2+high)/3,rm=(low+high*2)/3;
        if(!judge(lm))
        {
            low=lm;
            continue;
        }
        if(!judge(rm))
        {
            high=rm;
            continue;
        }
        if(check(lm)<check(rm))
            high=rm,ans=lm;
        else
            low=lm,ans=rm;
    }
    if(judge(low))
        m1=check(low);
    if(judge(high))
        m2=check(high);
    m1=min(m1,m2);
    printf("%d\n",min(m1,check(ans)));
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值