http://www.51nod.com/Challenge/Problem.html#!#problemId=1494
题解
一开始有start个人投自己,num表示当前已经收买了多少人,从大到小枚举自己以i张票当选,那么其他人的票数一定要小于i,拿钱收买他们花费最少的>=i的这部分人,再加上另外要收买的i-start-num个最便宜收买的花费就是当前答案,这里可以对每个人的收买代价建立权值线段树来查找收买前i-start-num个最便宜的花费。注意不是选自己的选民可以选别人的时候叛变花费可能为0,所以线段树应该从0开始。
1 #define dbg(x) cout<<#x<<" = "<< (x)<< endl 2 #define IO std::ios::sync_with_stdio(0); 3 #include <bits/stdc++.h> 4 #define iter ::iterator 5 using namespace std; 6 typedef long long ll; 7 typedef pair<ll,ll>P; 8 #define pb push_back 9 #define se second 10 #define fi first 11 #define rs o<<1|1 12 #define ls o<<1 13 const ll inf=0x7fffffff; 14 const int N=1e5+10; 15 int M; 16 priority_queue<ll,vector<ll>,greater<ll> >q[N]; 17 int id[N],vis[N],cnt; 18 ll start; 19 bool cmp(ll x,ll y){ 20 return q[x].size()>q[y].size(); 21 } 22 struct node{ 23 ll num,sum; 24 }a[N*4]; 25 void push(int o){ 26 a[o].num=a[ls].num+a[rs].num; 27 a[o].sum=a[ls].sum+a[rs].sum; 28 } 29 void up(int o,int l,int r,int p,ll v){ 30 if(l==r){ 31 a[o].num+=v; 32 a[o].sum+=v*l; 33 return; 34 } 35 int m=(l+r)/2; 36 if(p<=m)up(ls,l,m,p,v); 37 else up(rs,m+1,r,p,v); 38 push(o); 39 } 40 ll qu(int o,int l,int r,int p){ 41 if(p<=0)return 0; 42 if(a[1].num<p)return 2e9; 43 if(l==r)return 1ll*l*p; 44 int m=(l+r)/2; 45 if(p<=a[ls].num)return qu(ls,l,m,p); 46 else return a[ls].sum+qu(rs,m+1,r,p-a[ls].num); 47 } 48 int main(){ 49 scanf("%d",&M); 50 for(int i=1;i<=M;i++){ 51 ll x,y; 52 scanf("%lld%lld",&x,&y); 53 if(!x){ 54 start++; 55 continue; 56 } 57 q[x].push(y); 58 up(1,0,50000,y,1); 59 if(!vis[x]){ 60 id[++cnt]=x; 61 vis[x]=1; 62 } 63 } 64 sort(id+1,id+1+cnt,cmp); 65 ll ans=1e18,res=0,num=0; 66 for(int i=M;i>=max(1ll,start);i--){ 67 for(int j=1;j<=cnt;j++){ 68 int x=id[j]; 69 if(q[x].size()<i)break; 70 while(q[x].size()>=i){ 71 ll y=q[x].top(); 72 res+=y; 73 up(1,0,50000,y,-1); 74 q[x].pop(); 75 num++; 76 } 77 } 78 ll r=qu(1,0,50000,i-num-start); 79 ans=min(ans,res+r); 80 } 81 printf("%lld\n",ans); 82 }