51nod 1494 选举拉票 | 线段树
2017-10-18 17:18
344 查看
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('\n') #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 ; int mx, n, ans = INF, adj , nxt , sze , pol ; int val , 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; }
相关文章推荐
- 【51nod】1494 选举拉票 扫描线+线段树
- 51nod 1494 选举拉票【贪心】【扫描线】【线段树】
- [51nod] 1494 选举拉票 #算法设计策略
- 51nod 1494 选举拉票&&cf458C
- 51Nod-1494-选举拉票
- [枚举 线段树] 51Nod1494 选举拉票
- 51Nod - 1559 线段树 + 扫描线
- 51nod 1287 线段树
- 51nod 1206 Picture 矩形周长求并 | 线段树 扫描线
- 51nod 1364 最大字典序排列(线段树)
- 51nod 1206 1028 1494题解+扫描线模板
- 51nod 1611 金牌赛事&&cf115E [线段树]
- 51Nod 1672 扫描线 + 线段树/树状数组
- HDU5828(线段树好题,区间加,区间求根号,吉老师在51nod直播讲过这道题,相应的还有区间取模)
- 51nod 1562 玻璃切割 【线段树】
- 51nod 1571 最近等对 | 线段树 离线
- 51nod 1174区间中最大的数(线段树)
- 51nod 1819 黑白树 V2 树链剖分维护轻儿子信息+线段树
- 51nod 1672 区间交【线段树、multiset】
- 51nod 1766 树上的最远点对——线段树