把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【BZOJ4449】[Neerc2015] Distance on Triangulation(分治+BFS)

点此看题面

大致题意: 给定一个多边形以及一组三角剖分,每条边边权为\(1\),每次询问两点间最短路。

前言

写得心态爆炸当场去世。。。

推荐还是去写点分治吧(虽然我并不知道怎么写),我这种做法要出人命的。。。

大致想法

有一道和这题非常相似的题目:【BZOJ4456】[ZJOI2016] 旅行者

我们可以和上面这题一样,采用分治的做法,每次选择中间的剖分边,把多边形分成两部分。(具体思路可以直接参考上面那道题)

这题唯一一个比较好的地方,就是因为边权都是\(1\),可以直接\(BFS\),而不用写最短路。

但是,这道题和上面那题比起来,真的麻烦了很多。

要注意,此题多边形被分成两部分,不光要把询问分成两部分,还要把多边形的点以及剖分边也给分成两部分,于是一下多了很多细节。

具体的细节还是详见代码吧。

代码

#pragma GCC optimize(2)
#pragma GCC optimize("inline") 
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 50000
#define Q 100000
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
#define Gmin(x,y) (x>(y)&&(x=(y)))
#define Swap(x,y) (x^=y^=x^=y)
#define pb push_back
using namespace std;
int n,Qt,In[N+5],ee,lnk[N+5],que[N+5],dis[N+5];struct edge {int to,nxt;}e[4*N+5];
struct S {int x,y;I bool operator < (Con S& o) Con {return x^o.x?x<o.x:y>o.y;}}s[N+5];
int p[N+5],ans[Q+5];struct Qry {int p,x,y;}q[Q+5],ql[Q+5],qr[Q+5];S sl[N+5],sr[N+5];
class FastIO
{
	private:
		#define FS 100000
		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
		#define pc(c) (C==E&&(clear(),0),*C++=c)
		#define D isdigit(c=tc())
		int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
	public:
		I FastIO() {A=B=FI,C=FO,E=FO+FS;}
		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
		Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
		Tp I void writeln(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);pc('\n');}
		I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
}F;
I void BFS(CI s,Con vector<int>& v)//BFS求最短路
{
	static int ti=0;RI i,sz,k,H=1,T=1;++ti;
	for(i=0,sz=v.size();i^sz;++i) In[v[i]]=ti,dis[v[i]]=-1;dis[que[1]=s]=0;
	W(H<=T) for(i=lnk[k=que[H++]];i;i=e[i].nxt)
		!~dis[e[i].to]&&In[e[i].to]==ti&&(dis[que[++T]=e[i].to]=dis[k]+1);
}
I int find(CI p,CI x,CI y)//二分一条剖分线一侧的剖分边数
{
	RI l,r,mid,t1,t2;
	l=x,r=p-1;W(l<r) s[mid=l+r+1>>1]<s[p]?l=mid:r=mid-1;t1=l;//找到左边左侧的剖分边
	l=p-1,r=y;W(l<r) s[mid=l+r-1>>1].x>=s[p].y?r=mid:l=mid+1;t2=r;//找到右边左侧的剖分边
	return (t1-x+1)+(y-t2+1);//返回答案
}
vector<int> v[50];I void Solve(CI x,CI y,CI d,CI L,CI R)//分治
{
	RI i,sz=v[d].size(),wl=0,wr=0,tl=0,tr=0;vector<int> vl,vr;
	if(x>y||!sz||L>R) return;RI mid=x,X,Y;p[x]=find(x,x,y);//判边界
	#define Calc(id) max(p[id],y-x+1-p[id])
	for(i=x+1;i<=y;++i) p[i]=find(i,x,y),Calc(i)<Calc(mid)&&(mid=i);//找到中间的剖分边
	for(BFS(X=s[mid].x,v[d]),i=L;i<=R;++i) Gmin(ans[q[i].p],dis[q[i].x]+dis[q[i].y]);//最短路更新答案
	for(BFS(Y=s[mid].y,v[d]),i=L;i<=R;++i) Gmin(ans[q[i].p],dis[q[i].x]+dis[q[i].y]);//最短路更新答案
	for(i=x;i<=y;++i) i^mid&&(((s[i].x<X||s[i].y>Y)?sl[++wl]:sr[++wr])=s[i],0);//分边
	for(i=1;i<=wl;++i) s[x+i-1]=sl[i];for(i=1;i<=wr;++i) s[x+wl+i-1]=sr[i];//放回原数组
	for(i=L;i<=R;++i)//分询问
		(q[i].x<X||q[i].x>Y)&&(q[i].y<X||q[i].y>Y)&&(ql[++tl]=q[i],0),
		q[i].x>=X&&q[i].x<=Y&&q[i].y>=X&&q[i].y<=Y&&(qr[++tr]=q[i],0);
	for(i=1;i<=tl;++i) q[L+i-1]=ql[i];for(i=1;i<=tr;++i) q[L+tl+i-1]=qr[i];//放回原数组
	for(i=0;i^sz;++i) (v[d][i]<=X||v[d][i]>=Y)&&(v[d+1].pb(v[d][i]),0);//找出左侧点
	Solve(x,x+wl-1,d+1,L,L+tl-1),vector<int>().swap(v[d+1]);//注意剖分边上的点要放入两边,因此得开vector
	for(i=0;i^sz;++i) v[d][i]>=X&&v[d][i]<=Y&&(v[d+1].pb(v[d][i]),0);//找出右侧点
	Solve(x+wl,y-1,d+1,L+tl,L+tl+tr-1),vector<int>().swap(v[d+1]);
}
int main()
{
	RI i;for(F.read(n),i=1;i^n;++i) add(i,i+1),add(i+1,i);add(n,1),add(1,n);//连边
	for(i=1;i<=n-3;++i) F.read(s[i].x,s[i].y),add(s[i].x,s[i].y),add(s[i].y,s[i].x);
	for(i=1;i<=n-3;++i) s[i].x>s[i].y&&Swap(s[i].x,s[i].y);sort(s+1,s+n-2);//给剖分边排序
	for(F.read(Qt),i=1;i<=Qt;++i) F.read(q[i].x,q[i].y),ans[q[i].p=i]=q[i].x^q[i].y?n:0;
	for(i=1;i<=n;++i) v[0].pb(i);Solve(1,n-3,0,1,Qt);
	for(i=1;i<=Qt;++i) F.writeln(ans[i]);return F.clear(),0;
}
posted @ 2020-06-01 14:27  TheLostWeak  阅读(115)  评论(0编辑  收藏  举报