51nod 1494 选举拉票 | 线段树

51nod1494 选举拉票


题面

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

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

题解

啊……线段树……绝对要写完多检查一下……
不然会Debug De很久都De不出来……
长太息以掩涕兮……哀Bug之难De……

这道题和51nod的另一道题——稳定桌很相似。网上的题解把这类题归入“扫描线法”中。

具体做法:从大到小枚举你一共得到多少选票,然后分两步:选票比你多的人,你一定需要抢夺所有多出来的选票,当然,对每个人要抢他们手里最便宜的选票;如果抢完选票,当前选票数仍不够你枚举的选票数的话,就在所有不在你手中的选票中拣最便宜的抢。

注意抢夺选票这个过程是单向的,也就是你不可能把抢来的选票还回去。那么我们维护一个数据结构,支持求最小的k个数的和、支持删除一个数即可。我用的是线段树求第k大+树状数组求和。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cctype>
using namespace std;
typedef long long ll;
#define space putchar(' ')
#define enter putchar('
')
#define INF 0x3f3f3f3f
template <class T>
bool read(T &x){
    char c;
    bool op = 0;
    while(c = getchar(), c < '0' || c > '9')
        if(c == '-') op = 1;
        else if(c == EOF) return 0;
    x = c - '0';
    while(c = getchar(), c >= '0' && c <= '9')
        x = x * 10 + c - '0';
    if(op) x = -x;
    return 0;
}
template <class T>
void write(T x){
    if(x < 0) x = -x, putchar('-');
    if(x >= 10) write(x / 10);
    putchar('0' + x % 10);
}
const int N = 200005;
struct people {
    int a, b;
    bool operator < (const people &obj) const{
        return b < obj.b;
    }
} peo[N];
int mx, n, ans = INF, adj[N], nxt[N], sze[N], pol[N];
int val[N], sum[4*N];
void add(int p, int x){
    while(p <= n) val[p] += x, p += p & -p;
}
int ask(int p){
    int ret = 0;
    while(p) ret += val[p], p -= p & -p;
    return ret;
}
void build(int k, int l, int r){
    if(l == r) return (void)(sum[k] = 1);
    int mid = (l + r) >> 1;
    build(k << 1, l, mid);
    build(k << 1 | 1, mid + 1, r);
    sum[k] = sum[k << 1] + sum[k << 1 | 1];
}
void erase(int k, int l, int r, int p){
    if(l == r) return (void)(sum[k] = 0);
    int mid = (l + r) >> 1;
    if(p <= mid) erase(k << 1, l, mid, p);
    else erase(k << 1 | 1, mid + 1, r, p);
    sum[k] = sum[k << 1] + sum[k << 1 | 1];
}
int query(int k, int l, int r, int p){
    if(l == r) return l;
    int mid = (l + r) >> 1;
    if(sum[k << 1] >= p) return query(k << 1, l, mid, p);
    else return query(k << 1 | 1, mid + 1, r, p - sum[k << 1]);
}
bool cmp(int a, int b){
    return sze[a] < sze[b];
}
int main(){
    read(n);
    build(1, 1, n);
    for(int i = 1; i <= n; i++)
        read(peo[i].a), read(peo[i].b);
    sort(peo + 1, peo + n + 1);
    for(int i = n; i; i--){
        nxt[i] = adj[peo[i].a];
        adj[peo[i].a] = i;
        sze[peo[i].a]++;
        mx = max(mx, peo[i].a);
        add(i, peo[i].b);
    }
    for(int i = 1; i <= mx; i++)
        pol[i] = i;
    sort(pol + 1, pol + mx + 1, cmp);
    for(int tot = n, cost = 0, cnt = 0; tot >= 0; tot--){
        for(int i = mx; i && sze[pol[i]] >= tot; i--)
            for(int &e = adj[pol[i]]; e && sze[pol[i]] >= tot; e = nxt[e])
                add(e, -peo[e].b), erase(1, 1, n, e), cost += peo[e].b, sze[pol[i]]--, cnt++;
        if(cnt >= tot) ans = min(ans, cost);
        else ans = min(ans, cost + ask(query(1, 1, n, tot - cnt)));
    }
    write(ans), enter;
    return 0;
}
原文地址:https://www.cnblogs.com/RabbitHu/p/51nod1494.html