E2. Voting (Hard Version)(优先队列 买卖决策)

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);
    }
}

你可能感兴趣的:(想法题)