题目链接:点我啊╭(╯^╰)╮
题目大意:
要获得 n n n 个人所有的投票
对于一个人,如果已经有 m m m 个人投了票,则这个人可以免费投票
或者花费 p p p 让这个人投票
求最小花费
解题思路:
通过贪心选取,可以发现
对于一个还未投票的人 i i i ,最贪心的方式是在 ≥ m i ≥m_i ≥mi? 的人里选一部分人
让当前人数到达 m i mi mi 后,让这个人免费投票
我们不妨倒过来思考,反向枚举 m m m
对于一个 m i m_i mi? ,我们肯定是已经让小于 m i m_i mi? 的人全都投了票
然后将还未投票的(也就是 ≥ m i ≥m_i ≥mi? 的人) p p p 放到优先队列里
在优先队列里选取一部分人,让当前人数到达 m i m_i mi?
因此, q . s i z e ( ) q.size() q.size() 表示还未投票的, n ? q . s i z e ( ) n-q.size() n?q.size() 表示已经投票的人数
从队列里贪心选取较小的 p p p,直到 n ? q . s i z e ( ) ≥ m i n - q.size() ≥ m_i n?q.size()≥mi?
核心:反向思维
#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
using pii = pair <ll,int>;
const int maxn = 2e5 + 5;
int T, n;
vector <int> a[maxn];
priority_queue <int> q;int main() {scanf("%d", &T);while(T--){scanf("%d", &n);for(int i=0; i<=n; i++) a[i].clear();for(int i=1; i<=n; i++) {int p, m;scanf("%d%d", &m, &p);a[m].push_back(p);}while(q.size()) q.pop();ll ans = 0;for(int i=n; ~i; i--){for(auto p : a[i]) q.push(-p);while(n - q.size() < i) {ans -= q.top();q.pop();}}printf("%lld\n", ans);}
}