【题解】巴邻旁之桥

Problem

( ext{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('
');
		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('
');
		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('
');
	}
	return 0;
}
原文地址:https://www.cnblogs.com/h-lka/p/14923021.html