51nod - 1494 选举拉票

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

 

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

思路:

将每个候选人的选票金额从大到小排序。开始时假定购买所有选票,然后依次尝试放弃选票,比如除自己外有三个候选人。则第一轮尝试放弃这三个候选人的各自的最大选票金额,第二轮尝试放弃这三个候选人的第二大选票金额,依次累进。在每次放弃时,将放弃的选票金额放进线段树。在每轮放弃后,要判断是否满足当选条件,如果不满足,比如差a张选票,则需要从线段树中找到最小的a个算出总代价。

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
 
const int MAXA = 1e5 + 10;
const int MAXB = 1e4 + 10;
#define lson (root << 1)
#define rson (root << 1 | 1)
 
struct Node
{
	int val;
	int num;
};

int n;
Node tree[MAXB << 2];
vector<int> cost[MAXA];
vector<int> buf[MAXA];
 
void update(int root, int left, int right, int x)
{
	if (left == right)
	{
		tree[root].val += left;
		tree[root].num += 1;
		return;
	}

	int mid = left + (right - left) / 2;
	if (x <= mid)
	{
		update(lson, left, mid, x);
	}
	else
	{
		update(rson, mid + 1, right, x);
	}

	tree[root].val = tree[lson].val + tree[rson].val;
	tree[root].num = tree[lson].num + tree[rson].num;
}
 
int query(int root, int left, int right, int count)
{
	if (left == right)
	{
		return count * left;
	}

	int mid = left + (right - left) / 2;
	if (count <= tree[lson].num)
	{
		return query(lson, left, mid, count);
	}
	else
	{
		return tree[lson].val + query(rson, mid + 1, right, count - tree[lson].num);
	}
}

int main()
{
	cin >> n;
    int ans = 0, mx_num = 0, mx_cost = 1, a, b;
    for (int i = 1; i <= n; i++)
    {
		cin >> a >> b;
        if (b == 0)
        {
            continue;
        }
 
        ans += b;
        mx_num = max(mx_num, a);
        mx_cost = max(mx_cost, b);
        cost[a].push_back(b);
    }
 
    int mx_size = 0;
    for (int i = 1; i <= mx_num; i++)
    {
        if (cost[i].size())
        {
            sort(cost[i].begin(), cost[i].end(), greater<int>());
 
            mx_size = max(mx_size, (int)cost[i].size());
            for (int j = 0; j < cost[i].size(); j++)
            {
                buf[j].push_back(cost[i][j]);
            }
        }
    }
 
    int k = n, res = ans;
    for (int i = 0; i < mx_size; i++)
    {
        k -= buf[i].size();
        for (int j = 0; j < buf[i].size(); j++)
        {
            update(1, 1, mx_cost, buf[i][j]);
            res -= buf[i][j];
        }
 
        int tmp = 0, mn;
        if (k <= i + 1)
        {
            mn = min(i + 2 - k, n);   
            tmp = query(1, 1, mx_cost, mn);
        }
 
        ans = min(ans, res + tmp);
    }
 
	cout << ans << endl;
 
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值