CF 457C Elections 解题报告(线段树)

这篇博客介绍了如何运用线段树数据结构解决一道竞赛题目——CF 457C Elections。内容包括问题描述、解题思路和代码实现,主要讨论如何在有限预算下通过贿赂选民赢得选举。

C. Elections
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

You are running for a governor in a small city in Russia. You ran some polls and did some research, and for every person in the city you know whom he will vote for, and how much it will cost to bribe that person to vote for you instead of whomever he wants to vote for right now. You are curious, what is the smallest amount of money you need to spend on bribing to win the elections. To win elections you need to have strictly more votes than any other candidate.

Input

First line contains one integer n (1 ≤ n ≤ 105) — number of voters in the city. Each of the next n lines describes one voter and contains two integers ai and bi (0 ≤ ai ≤ 105; 0 ≤ bi ≤ 104) — number of the candidate that voter is going to vote for and amount of money you need to pay him to change his mind. You are the candidate 0 (so if a voter wants to vote for you, ai is equal to zero, in which case bi will also be equal to zero).

Output

Print one integer — smallest amount of money you need to spend to win the elections.

Sample test(s)
input
5
1 2
1 2
1 2
2 1
0 0
output
3
input
4
1 2
1 2
2 1
0 0
output
2
input
1
100000 0
output
0

    解题报告: 昨晚(准确来说是今天早上)CF上的比赛第三题。前两题1小时多搞定了,这题一直没有想法。比赛后就去睡了。白天看了前几名的代码,然后就有点头绪了。

    我们的目标是用最少的钱使自己的票数多余其他人的票数。我们可以直接买票比我们当前票数多的那些人的票,这样我们的票增加了,他们的票减少了;我们也可以直接买最便宜的票,直到超过所有其他人。这样我们可以枚举最后其他人的票最多有多少。减少的票我们都买下来,不够我们就继续买最便宜的。最终取结果的最小值。

    代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <iomanip>
using namespace std;
#define ff(i, n) for(int i=0;i<(n);i++)
#define fff(i, n, m) for(int i=(n);i<=(m);i++)
#define dff(i, n, m) for(int i=(n);i>=(m);i--)
#define fit(it, n) for(auto it = (n).begin(); it != (n).end(); it++)
#define bit(n) (1LL<<(n))
typedef long long LL;
typedef unsigned long long ULL;
void work();
int main()
{
#ifdef ACM
    freopen("in.txt", "r", stdin);
#endif // ACM
    work();
}

/***************************************************/

#define lson l, m, pos<<1
#define rson m+1, r, pos<<1|1
const int maxn = 111111;
int num[maxn<<2];
int pri[maxn<<2];

int p[maxn], si[maxn];

bool cmp(int a, int b)
{
    return si[a] > si[b];
}

void updateFather(int pos)
{
    num[pos] = num[pos<<1] + num[pos<<1|1];
    pri[pos] = pri[pos<<1] + pri[pos<<1|1];
}

void update(int p, int x, int l = 0, int r = 10000, int pos = 1)
{
    if (l == r)
    {
        num[pos] += x;
        pri[pos] += x * p;

        return;
    }

    int m = (l+r)/2;
    if (p <= m)
        update(p, x, lson);
    else
        update(p, x, rson);

    updateFather(pos);
}

int query(int k, int l = 0, int r = 10000, int pos = 1)
{
    if (k == 0) return 0;
    if (l == r) return pri[pos]/num[pos]*k;
    if (k == num[pos]) return pri[pos];

    int m = (l+r)/2;
    if (k <= num[pos<<1])
        return query(k, lson);
    else
        return pri[pos<<1] + query(k-num[pos<<1], rson);
}

void work()
{
    int n;
    while (scanf("%d", &n) == 1)
    {
        memset(num, 0, sizeof(num));
        memset(pri, 0, sizeof(pri));

        map<int, multiset<int>> vote;
        ff (i, n)
        {
            int a, b;
            scanf("%d%d", &a, &b);
            if(a) vote[a].insert(b);
            update(b, 1);
        }

        int L = 0;
        fit (it, vote)
        {
            p[L++] = it->first;
            si[it->first] = it->second.size();
        }
        sort(p, p+L, cmp);

        int ans = 2e9;
        int vnum = 0, vsum = 0;
        int end = 0;
        dff(i, n, 1)
        {
            while(end < L && si[p[end]] >= i) end ++;
            ff(j, end)
            {
                auto it = vote[p[j]].begin();
                int price = *it;
                vnum ++;
                vsum += price;
                update(price, -1);
                vote[p[j]].erase(it);
            }
            ans = min(ans, vsum + query(max(i-vnum, 0)));
        }

        printf("%d\n", ans);
    }
}


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值