扫描线是什么鬼啊……为什么我好像什么都不会啊QwQ……
以上的图片来自sillyf大佬的博客,大佬传送门
把所有的候选人拥有的票数看做一条线段,枚举当前自己拥有的票数为
i
<script type="math/tex" id="MathJax-Element-2">i</script>,所有票数大于等于自己的票数的人必须要去py(逃)收买投票的人
如果把当前必须买的部分全买完了票数还是不够,就需要去挑着买的部分选一些便宜的买来,这个可以用权值线段树实现。
附上AC代码:
#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int n,x,y,num,m,c,t1[N<<2];
vector <int> a[N],b[N];
ll sum,ans,t2[N<<2];
inline char nc(void){
static char ch[100010],*p1=ch,*p2=ch;
return p1==p2&&(p2=(p1=ch)+fread(ch,1,100010,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &a){
static char c=nc();int f=1;
for (;!isdigit(c);c=nc()) if (c=='-') f=-1;
for (a=0;isdigit(c);a=(a<<3)+(a<<1)+c-'0',c=nc());
return (void)(a*=f);
}
inline bool cmp(int x,int y){return x>y;}
#define lt (k<<1)
#define rt (k<<1|1)
#define mid (l+r>>1)
inline void change(int k,int l,int r,int wz){
if (l==r) return (void)(++t1[k],t2[k]+=1ll*l);
if (wz<=mid) change(lt,l,mid,wz);
else change(rt,mid+1,r,wz);
return (void)(t1[k]=t1[lt]+t1[rt],t2[k]=t2[lt]+t2[rt]);
}
inline ll query(int k,int l,int r,int w){
if (l==r) return 1ll*l*w;
if (w==t1[lt]) return t2[lt];
if (w<t1[lt]) return query(lt,l,mid,w);
return t2[lt]+query(rt,mid+1,r,w-t1[lt]);
}
int main(void){
read(n),m=1;
for (int i=1; i<=n; ++i){
read(x),read(y),num=max(num,x);
if (y==0) continue;
m=max(m,y),ans+=1ll*y,a[x].push_back(y);
}
for (int i=1; i<=num; ++i)
if (a[i].size()){
sort(a[i].begin(),a[i].end(),cmp);
for (int j=0; j<a[i].size(); ++j) b[j+1].push_back(a[i][j]);
}
c=n,sum=ans;
for (int i=1; i<=n; ++i){
c-=b[i].size();
for (int j=0; j<b[i].size(); ++j) change(1,1,m,b[i][j]),sum-=1ll*b[i][j];
ll t=0;
if (c<=i) t=query(1,1,m,min(i+1-c,n));
ans=min(ans,sum+t);
}
return printf("%lld\n",ans),0;
}