http://codeforces.com/problemset/problem/1251/E2
题意:
有n个人,每个人有一个m,val,征服这个人需要val,如果你至少征服了其它m个人,则可以直接征服这个人,问征服所有人的最少花费。
解析:
以前多校做过类似的一道题,做法是按照顺序,先将当前的点买了,然后加入反悔队列,到了后面如果多余了从队列中选一个花费最大的退掉。
这道题也差不多,我们将m值相同的当作一类,从大往小做。
举个例子,假设m值分别为 [ 5 , 5 , 5 , 3 , 3 , 1 ] [5,5,5,3,3,1] [5,5,5,3,3,1],那么说明即使买完 [ 3 , 3 , 1 ] [3,3,1] [3,3,1], [ 5 , 5 , 5 ] [5,5,5] [5,5,5]之中也要买两个。所以先将必须买的买了。
到了后面,如果存在必须买的人,我们可以选择m大的来买,所以刚才没有买的人中也可能用到。这些人用优先队列来维护。
相当于维护预购队列,等之后还不够的时候从队列中取一个便宜的。
怎么看是否必须买呢?用res维护剩余的人(m小于当前),have表示已经买了的人,当 h a v e + r e s < i have+reshave+res<i,说明当前剩余的人不能直接征服,所以还有买。
代码:
#include
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define LL long long
const int maxn=2e5+9;
vector<int>V[maxn];
int main(){
int t;scanf("%d",&t);
while(t--){
int n;scanf("%d",&n);
rep(i,0,n-1)V[i].clear();
rep(i,1,n){
int m,val;
scanf("%d%d",&m,&val);
V[m].push_back(val);
}
priority_queue<int,vector<int>,greater<int> >Q;
int have=0,res=n;
LL ans=0;
per(i,n-1,0){
res-=V[i].size();
for(auto P:V[i])Q.push(P);
while(have+res<i){
ans+=Q.top();Q.pop();
have++;
}
}
printf("%lld\n",ans);
}
}