题目链接:点我啊╭(╯^╰)╮
题目大意:
要获得
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);
}
}