【題解】巴鄰旁之橋
阿新 • • 發佈:2021-06-23
\(\text{Solution:}\)
當\(k=1\)的時候,中位數就可以解決問題。
這引起思考:\(k=2\)是不是一個拓展版?
考慮無論將線段按照左還是右端點排序都不能滿足要求,於是我們發現:當橋靠近一個線段的中點的時候,走它一定會優。
所以,我們將線段按照中點排序,並考慮列舉分界點,這樣左右兩邊就被劃分成了兩個\(k=1\)的子問題。
這是個動態的求中位數問題。考慮 FHQ_Treap 解決。
維護兩棵樹,以座標為序,一顆維護分界點左邊的,一顆維護分界點右邊的。這樣我們只需要統計出每次的答案就可以了。
那如何統計答案?
將點畫在座標軸上,觀察發現:要求它們到一條線的距離,只需要:求出線右邊所有座標到線的距離 加上 線左邊所有座標到線的距離 即可。
那麼以值為序的同時,我們可以維護 siz 和 sum 來實現這一點。
關鍵在於:思考到拓展思路並理解按照中點排序的意義 以及 如何快速統計答案。
總之,畫個圖很重要。
#include<bits/stdc++.h> using namespace std; const int MAXN=3e5+10; inline int read() { int s=0; char ch=getchar(); while(!isdigit(ch))ch=getchar(); while(isdigit(ch)) { s=s*10-48+ch; ch=getchar(); } return s; } inline void write(long long x){ if(x<0)putchar('-'); if(x>9)write(x/10); putchar(x%10+48); } struct Line { int s,t,mid; bool operator<(const Line&B)const { return mid<B.mid; } }; vector<Line>v; inline int rd() { return rand()<<15|rand(); } struct Tree { int cnt,rt,siz[MAXN],tr[MAXN][2],cv[MAXN]; int val[MAXN]; long long sum[MAXN]; inline int build(int v) { siz[++cnt]=1; cv[cnt]=rd(); val[cnt]=v; sum[cnt]=v; return cnt; } inline void pushup(int x) { siz[x]=siz[tr[x][1]]+siz[tr[x][0]]+1; sum[x]=sum[tr[x][1]]+sum[tr[x][0]]+val[x]; } int merge(int x,int y) { if(!x||!y)return x+y; if(cv[x]<cv[y]) { tr[x][1]=merge(tr[x][1],y); pushup(x); return x; } else { tr[y][0]=merge(x,tr[y][0]); pushup(y); return y; } } void split(int now,int k,int &x,int &y) { if(!now) { x=y=0; return; } if(val[now]<=k)x=now,split(tr[now][1],k,tr[now][1],y); else y=now,split(tr[now][0],k,x,tr[now][0]); pushup(now); } int kth(int now,int k) { if(k<=siz[tr[now][0]])return kth(tr[now][0],k); else if(k==siz[tr[now][0]]+1)return val[now]; else return kth(tr[now][1],k-siz[tr[now][0]]-1); } void Ins(int v) { int x,y; split(rt,v,x,y); rt=merge(merge(x,build(v)),y); } void del(int v){ int x,y,z; split(rt,v,x,y); split(x,v-1,x,z); z=merge(tr[z][0],tr[z][1]); rt=merge(merge(x,z),y); } } T[2]; int K,N; long long ans; long long res[MAXN]; char P[2],Q[2]; inline int Abs(int x) { if(x<0)x=-x; return x; } inline int Min(int x,int y) {return x-y>0?y:x;} inline long long Lmin(long long x,long long y){return x-y>0?y:x;} int main() { K=read(); N=read(); for(int i=1; i<=N; ++i) { char P,Q; int x,y; cin>>P>>x>>Q>>y; if(P==Q) { ans+=1ll*Abs(x-y); continue; } else { if(x>y)swap(x,y); Line S; S.s=x; S.t=y; S.mid=x+y; v.push_back(S); } } ans+=(long long)v.size(); sort(v.begin(),v.end()); if((int)v.size()==0){ write(ans);putchar('\n'); return 0; } if(K==1) { vector<int>V; for(int i=0; i<(int)v.size(); ++i)V.push_back(v[i].s),V.push_back(v[i].t),ans+=1ll*(v[i].t-v[i].s); sort(V.begin(),V.end()); int All=(int)V.size(); All>>=1; int pos=V[All]; for(int i=0; i<(int)v.size(); ++i) { if(pos>=v[i].s&&pos<=v[i].t)continue; long long dt=Min(Abs(pos-v[i].s),Abs(pos-v[i].t)); dt<<=1ll; ans+=dt; } write(ans);putchar('\n'); return 0; } else { for(int i=1; i<(int)v.size(); ++i) { T[1].Ins(v[i].s); T[1].Ins(v[i].t); } T[0].Ins(v[0].s); T[0].Ins(v[0].t); for(int i=1; i<(int)v.size()-1; ++i) { T[1].del(v[i].s); T[1].del(v[i].t); T[0].Ins(v[i].s); T[0].Ins(v[i].t); int num=T[0].siz[T[0].rt]; num>>=1; int v=T[0].kth(T[0].rt,num); int Tx,Ty; T[0].split(T[0].rt,v,Tx,Ty); int lsiz=T[0].siz[Tx]; int rsiz=T[0].siz[Ty]; res[i]=1ll*lsiz*v-T[0].sum[Tx]; res[i]+=T[0].sum[Ty]-1ll*rsiz*v; T[0].rt=T[0].merge(Tx,Ty); num=T[1].siz[T[1].rt]; num>>=1; v=T[1].kth(T[1].rt,num); int TTx,TTy; T[1].split(T[1].rt,v,TTx,TTy); lsiz=T[1].siz[TTx]; rsiz=T[1].siz[TTy]; res[i]+=1ll*lsiz*v-T[1].sum[TTx]; res[i]+=T[1].sum[TTy]-1ll*rsiz*v; T[1].rt=T[1].merge(TTx,TTy); } long long Ans=(1LL<<60); for(int i=1;i<(int)v.size()-1;++i)Ans=Lmin(Ans,res[i]); Ans+=ans; write(Ans);putchar('\n'); } return 0; }