Codeforces 1251 E2. Voting (Hard Version) —— 线段树,找区间最小值的位置

This way

题意:

现在有n个人,每个人都有两个值p,m,你现在要买通所有人,第i个人会跟随你当且仅当你买通的人的个数>=mi或者用pi去买通他。问你最少要花多少钱去买通所有人。

题解:

先对所有人按照m从小到大排序,因为我们买通的人一定是从少到多的。然后到第i个人的时候假设我们把前面i-1个人都已经买通了,看看还差多少人,这个数字就是我们需要在i~n这些人中买通的人的最少个数。这样的正确性在于所有的m<n,并且因为我们已经排序了,后面的人的m一定是大于等于当前的人的,对于第i个人如果不能直接跟你走,那么你仅仅买通他可能也无济于事,需要在后面至少买通相差的人数。然后我们从后往前做,并且用num维护当前已经买通了多少人。
找买通哪些人的话,就用线段树查询区间最小值的位置就行了,然后注意买通了的话就要更新值。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e5+5;
ll mi[N*4];
struct node{
    int v;
    ll p;
    bool operator< (const node& a)const {
        return v<a.v;
    }
}a[N];
void build(int l,int r,int root){
    if(l==r){
        mi[root]=a[l].p;
        return ;
    }
    int mid=l+r>>1;
    build(l,mid,root<<1);
    build(mid+1,r,root<<1|1);
    mi[root]=min(mi[root<<1],mi[root<<1|1]);
}
void update(int l,int r,int root,int p,ll v){
    if(l==r){
        mi[root]=v;
        return ;
    }
    int mid=l+r>>1;
    if(mid>=p)
        update(l,mid,root<<1,p,v);
    else
        update(mid+1,r,root<<1|1,p,v);
    mi[root]=min(mi[root<<1],mi[root<<1|1]);
}
int query(int l,int r,int root,int ql,int qr){
    if(l>=ql&&r<=qr){
        if(l==r)
            return l;
        int mid=l+r>>1;
        if(mi[root<<1]==mi[root])
            return query(l,mid,root<<1,ql,qr);
        else
            return query(mid+1,r,root<<1|1,ql,qr);
    }
    int mid=l+r>>1,p1,p2;
    if(mid>=ql&&mid<qr){
        p1=query(l,mid,root<<1,ql,qr),p2=query(mid+1,r,root<<1|1,ql,qr);
        if(a[p1].p>a[p2].p)
            p1=p2;
    }
    else if(mid>=ql)
        p1=query(l,mid,root<<1,ql,qr);
    else
        p1=query(mid+1,r,root<<1|1,ql,qr);
    return p1;
}
struct Query{
    int l,num;
}q[N];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d%lld",&a[i].v,&a[i].p);
        sort(a+1,a+1+n);
        build(1,n,1);
        int l=1,cnt=0;
        for(int i=1;i<=n;){
            q[++cnt]={i,a[i].v-i+1};
            int v=a[i].v;
            while(i<=n&&v==a[i].v)
                i++;
        }
        int num=0;
        ll ans=0;
        for(int i=cnt;i;i--){
            if(num>=q[i].num)continue;
            q[i].num-=num;
            while(q[i].num--){
                int p=query(1,n,1,q[i].l,n);
                ans+=a[p].p;
                update(1,n,1,p,1e9+1);
                a[p].p=1e9+1;
                num++;
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/tianyizhicheng/article/details/107198232