题意:
有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;
}