[51nod] 1494 选举拉票 #算法设计策略

1494 选举拉票

题目来源: CodeForces 基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题

现在你要竞选一个县的县长。你去对每一个选民进行了调查。你已经知道每一个人要选的人是谁,以及要花多少钱才能让这个人选你。现在你想要花最少的钱使得你当上县长。你当选的条件是你的票数比任何一个其它候选人的多(严格的多,不能和他们中最多的相等)。请计算一下最少要花多少钱。

Input
单组测试数据。
第一行有一个整数n (1 ≤ n ≤ 10^5),表示这个县的选民数目。
接下来有n行,每一行有两个整数ai 和 bi (0 ≤ ai ≤ 10^5; 0 ≤ bi ≤ 10^4),表示第i个选民选的是第ai号候选人,想要让他选择自己就要花bi的钱。
你是0号候选人(所以,如果一个选民选你的话ai就是0,这个时候bi也肯定是0)。
Output
输出一个整数表示花费的最少的钱。
Input示例
5
1 2
1 2
1 2
2 1
0 0
Output示例
3
Analysis分析
正解:算法设计策略 + 线段树
为什么特别标上这个算法设计策略呢
这道题思路实在太复杂qwq
我们可以想象有多个河内塔,一个塔就是一个候选人,塔中一片就是一个选民,候选人的“决心”越大片越大
因此我们这样排序:每个候选人的选票小票在上大票在下(笑)
那么我们怎么抢票呢?
宏观策略是这样的:抢票数最高的候选人最便宜的票
因此就是从每个塔的顶端拿选票堆在自己的塔上,直到自己的塔最高
所以这个东西想了我一个下午+一个晚上
所以我最后去看题解了(听说CCZ和SXT是第四个和第五个AC的orz)
题解的意思是这样:从大到小枚举最终得票数, 然后根据每次的不同选择计算出来的结果更新答案
那么我枚举到自己有 i 张票的时候,需要保证其他候选人的票 < i
所以他们多出来的票我就都收了(当然收的是最便宜的,这里用优先队列维护)
砍完候选人的票之后,我的票可能仍然不够 i 张,定义我之前抢过来的票数为 num ,
那么从票仓里再提取出 i - num + 投自己的最初的选票 张票即可
当然这里是提取前 k 张最小的
这里可以用权值线段树维护
这样,我们从大到小枚举,因此枚举到第 i-1 张时,可以直接继承第 i 张的投票情况,无需初始化
详情请看代码
注意,票价是可以为 0 且不为自己选民的
 
Code代码
 1 #include<stdio.h>
 2 #include<queue>
 3 #include<algorithm>
 4 #include<iostream>
 5 #define mid (L+R)/2
 6 #define lc (rt<<1)
 7 #define rc (rt<<1|1)
 8 #define maxn 200000
 9 using namespace std;
10 
11 long long n,list[maxn],qwq;
12 bool vis[maxn];
13 
14 struct nodd{
15     long long num,sum;
16 }T[maxn*4];
17 
18 void modify(long long rt,long long L,long long R,long long pos,long long val){
19     if(pos < L || pos > R) return;
20     if(L == R) T[rt].num += val,T[rt].sum += val*pos;
21     else{
22         /*if(pos <= mid)*/ modify(lc,L,mid,pos,val);
23         /*else*/ modify(rc,mid+1,R,pos,val);
24         T[rt].num = T[lc].num+T[rc].num;
25         T[rt].sum = T[lc].sum+T[rc].sum;
26     }
27 }
28 
29 long long query(long long rt,long long L,long long R,long long pos){
30     if(pos <= 0) return 0;
31     if(pos > T[rt].num) return 2e9;
32     if(L == R) return L*pos;//T[rt].sum;
33     else if(pos <= T[lc].num) return query(lc,L,mid,pos);
34     else return T[lc].sum + query(rc,mid+1,R,pos-T[lc].num);
35 }
36 
37 priority_queue<long long,vector<long long> ,greater<long long> > heap[maxn];
38 
39 bool cmp1(long long A,long long B){ return heap[A].size() > heap[B].size(); }
40 
41 int main(){
42     scanf("%lld",&n);
43     
44     for(int i = 1;i <= n;i++){
45         long long x,y;
46         scanf("%lld%lld",&x,&y);
47         heap[x].push(y);
48         if(x&&!vis[x]) list[++qwq] = x,vis[x] = true;
49         if(x) modify(1,0,20000,y,1);
50     }
51     
52     sort(list+1,list+1+qwq,cmp1);
53 //    cout << "list";
54 //    for(int i = 1;i <= qwq;i++) printf("%d ",list[i]); cout << endl;
55     long long s = heap[0].size(),num = 0,sum = 0,ans = 2e9;
56     
57     for(int i = n;i >= max(1LL,s);i--){
58         for(int j = 1;j <= qwq;j++){
59             if(heap[list[j]].size() < i) break;
60             while(heap[list[j]].size() >= i){
61                 sum += heap[list[j]].top();
62                 modify(1,0,20000,heap[list[j]].top(),-1);
63                 heap[list[j]].pop();
64                 num++;
65             }
66         }ans = min(ans,sum+query(1,0,20000,i-(num+s)));
67 //        printf("#%d: ans %d\n",i,ans);
68     }
69     
70     printf("%lld",ans);
71     
72     return 0;
73 }
高效算法设计策略

 

posted @ 2017-10-31 14:03  μSsia  阅读(351)  评论(0编辑  收藏  举报