中文题面
考虑把所有人的票分别从大到小排序之后可以看做n条线段
枚举自己的票数为i,所以每条线段超过i的部分必须收买,如果还不够就到前面挑不够的
0≤b[i]≤104
求前k小想到权值线段树(应该是类似的东西)
#include<cstdio>
#include<algorithm>
#include<vector>
#define N 100000
#define LL long long
using namespace std;
inline char nc() {
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x) {
char c=nc(),b=1;
for(;!(c>='0'&&c<='9');c=nc()) if(c=='-') b=-1;
for(x=0;c>='0'&&c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
vector<int>a[N+5],f[N+5];
int n,m,num[N*4];
LL sum,ans,t[N*4];
void change(int x,int l,int r,int q) {
if(l==r) {
++num[x];
t[x]+=1ll*l;
return;
}
int mid=(l+r)>>1;
if(q<=mid) change(x<<1,l,mid,q);
else change(x<<1|1,mid+1,r,q);
t[x]=t[x<<1]+t[x<<1|1];
num[x]=num[x<<1]+num[x<<1|1];
}
LL query(int x,int l,int r,int q) {
if(l==r) return 1ll*l*q;
int mid=(l+r)>>1;
if(q==num[x<<1]) return t[x<<1];
if(q>num[x<<1]) return t[x<<1]+query(x<<1|1,mid+1,r,q-num[x<<1]);
else return query(x<<1,l,mid,q);
}
bool cmp(int x,int y) {return x>y;}
int main() {
read(n);
int maxn=1,peo=0;
ans=0;
for(int i=1,x,y;i<=n;++i) {
read(x),read(y);
peo=max(peo,x);
if(y==0) continue;
maxn=max(y,maxn);
ans+=1ll*y;
a[x].push_back(y);
}
for(int i=1;i<=peo;++i)
if(a[i].size()) {
sort(a[i].begin(),a[i].end(),cmp);
for(int j=0;j<a[i].size();++j)
f[j+1].push_back(a[i][j]);
}
int k=n;
LL tmp=ans;
for(int i=1;i<=n;++i) {
k-=f[i].size();
for(int j=0;j<f[i].size();++j)
change(1,1,maxn,f[i][j]),tmp-=1ll*f[i][j];
LL add=0;
if(k<=i)
add=query(1,1,maxn,min(i+1-k,n));
ans=min(ans,tmp+add);
}
printf("%lld\n",ans);
return 0;
}