题意:
现在有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;
}