您的位置:首页 > 其它

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

2017-10-31 14:03 267 查看

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代码
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<iostream>
#define mid (L+R)/2
#define lc (rt<<1)
#define rc (rt<<1|1)
#define maxn 200000
using namespace std;

long long n,list[maxn],qwq;
bool vis[maxn];

struct nodd{
long long num,sum;
}T[maxn*4];

void modify(long long rt,long long L,long long R,long long pos,long long val){
if(pos < L || pos > R) return;
if(L == R) T[rt].num += val,T[rt].sum += val*pos;
else{
/*if(pos <= mid)*/ modify(lc,L,mid,pos,val);
/*else*/ modify(rc,mid+1,R,pos,val);
T[rt].num = T[lc].num+T[rc].num;
T[rt].sum = T[lc].sum+T[rc].sum;
}
}

long long query(long long rt,long long L,long long R,long long pos){
if(pos <= 0) return 0;
if(pos > T[rt].num) return 2e9;
if(L == R) return L*pos;//T[rt].sum;
else if(pos <= T[lc].num) return query(lc,L,mid,pos);
else return T[lc].sum + query(rc,mid+1,R,pos-T[lc].num);
}

priority_queue<long long,vector<long long> ,greater<long long> > heap[maxn];

bool cmp1(long long A,long long B){ return heap[A].size() > heap[B].size(); }

int main(){
scanf("%lld",&n);

for(int i = 1;i <= n;i++){
long long x,y;
scanf("%lld%lld",&x,&y);
heap[x].push(y);
if(x&&!vis[x]) list[++qwq] = x,vis[x] = true;
if(x) modify(1,0,20000,y,1);
}

sort(list+1,list+1+qwq,cmp1);
//    cout << "list";
//    for(int i = 1;i <= qwq;i++) printf("%d ",list[i]); cout << endl;
long long s = heap[0].size(),num = 0,sum = 0,ans = 2e9;

for(int i = n;i >= max(1LL,s);i--){
for(int j = 1;j <= qwq;j++){
if(heap[list[j]].size() < i) break;
while(heap[list[j]].size() >= i){
sum += heap[list[j]].top();
modify(1,0,20000,heap[list[j]].top(),-1);
heap[list[j]].pop();
num++;
}
}ans = min(ans,sum+query(1,0,20000,i-(num+s)));
//        printf("#%d: ans %d\n",i,ans);
}

printf("%lld",ans);

return 0;
}
高效算法设计策略

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: