【题解】巴邻旁之桥
\(\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;
}