• 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;
    }
    
  • 相关阅读:
    hduoj1285确定比赛名次
    HDU 1041 Computer Transformation 数学DP题解
    使用Git【转】
    Git-标签管理【转】
    Git-分支管理【转】
    Git-远程仓库【转】
    Git-时光穿梭【转】
    创建版本库【转】
    安装Git【转】
    Git简介【转】
  • 原文地址:https://www.cnblogs.com/RabbitHu/p/51nod1494.html
Copyright © 2020-2023  润新知