Link:
Solution:
首先算出能提前算的贡献
$K=1$:肯定选中间的点,小学数学
$K=2$:对于每对$(x,y)$一定选离$(x+y)/2$近的桥
也就是说将$(x,y)$按$(x+y)/2$的值排序后一定恰有一个分割点使得两边选择不同的桥!
考虑如何如何快速枚举所有分割点时的答案:
需要支持插入、删除、求中位数及两边的和,明显选择$Splay$来维护(求和更容易些)
这样先将所有数对加进一个$Splay$,再不断将其中的数移向另一棵$Splay$即可
注意:可能以前的$Splay$板子容易出不少锅啊……
首先$Update$时最好对$z$进行判断,然后$Splay$最后也要$Pushup$否则会在树高低时不更新$size$
Code:
#include <bits/stdc++.h> using namespace std; #define X first #define Y second typedef double db; typedef long long ll; typedef pair<int,int> P; const int MAXN=2e5+10;//注意范围 const ll INF=1<<30; struct Splay { ll sum[MAXN]; int rt,tot,f[MAXN],sz[MAXN],cnt[MAXN],ch[MAXN][2],val[MAXN]; void update(int x) { sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+cnt[x]; sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+1ll*val[x]*cnt[x]; } void rotate(int x) { int y=f[x],z=f[y],k=(ch[y][1]==x); //最好对z特判一下防止对ch[0]操作 if(z) ch[z][ch[z][1]==y]=x;f[x]=z; ch[y][k]=ch[x][k^1];f[ch[x][k^1]]=y; ch[x][k^1]=y;f[y]=x; update(y);update(x); } void splay(int x,int up) { while(f[x]!=up) { int y=f[x],z=f[y]; if(z!=up) (ch[y][0]==x)^(ch[z][0]==y)?rotate(x):rotate(y); rotate(x); } if(!up) rt=x; //!!!!!!!! update(x);//可能f[x]=up,但也要update } void insert(int x) { int k=rt,anc=0; while(k&&val[k]!=x) anc=k,k=ch[k][x>val[k]]; if(k) cnt[k]++; else { k=++tot; if(anc) ch[anc][x>val[anc]]=k; val[k]=x;cnt[k]=1;f[k]=anc;sz[k]=1; } splay(k,0); } void find(int x) { int k=rt; while(x!=val[k]&&ch[k][x>val[k]]) k=ch[k][x>val[k]]; splay(k,0); } int kth(int x) { int k=rt; //对当前为空的情况特判 if(sz[k]<x||!x) return 0; while(true) { if(x>sz[ch[k][0]]+cnt[k]) x-=sz[ch[k][0]]+cnt[k],k=ch[k][1]; else if(x<=sz[ch[k][0]]) k=ch[k][0]; else return k; } } //不用加边界的删除法 void del(int x) { find(x); if(val[rt]!=x) return; if(cnt[rt]>1) cnt[rt]--; else if(!ch[rt][0]||!ch[rt][1]) { int k=ch[rt][0]+ch[rt][1]; f[k]=0;rt=k; } else { int k=ch[rt][0]; while(ch[k][1]) k=ch[k][1]; splay(k,rt); ch[k][1]=ch[rt][1]; f[ch[k][1]]=k; rt=k;f[k]=0; } //由于后面没有splay了要update update(rt); } ll query() { int k=kth(sz[rt]/2); if(!k) return 0;splay(k,0); ll ret1=1ll*val[k]*sz[ch[k][0]]-sum[ch[k][0]]; ll ret2=sum[ch[k][1]]-1ll*val[k]*sz[ch[k][1]]; return ret1+ret2; } }s1,s2; char a[10],b[10]; ll res=1ll<<60,pre; int n,x,y,k,tot;P dat[MAXN]; bool cmp(P a,P b){return a.X+a.Y<b.X+b.Y;}; int main() { scanf("%d%d",&k,&n); for(int i=1;i<=n;i++) { scanf("%s%d%s%d",a,&x,b,&y); if(a[0]==b[0]) pre+=abs(x-y); else dat[++tot]=P(x,y),pre++; } //注意特判!!! if(!tot) return printf("%lld",pre),0; sort(dat+1,dat+tot+1,cmp); for(int i=1;i<=tot;i++) s1.insert(dat[i].X),s1.insert(dat[i].Y); if(k==1) return printf("%lld",s1.query()+pre),0; for(int i=1;i<=tot;i++) { s1.del(dat[i].X);s1.del(dat[i].Y); s2.insert(dat[i].X);s2.insert(dat[i].Y); res=min(res,s1.query()+s2.query()); } printf("%lld",res+pre); return 0; }