LYZ Alan_cty HJY LZH DDX LYD LH samjia2000 WerKeyTom_FTD a_crazy_czy Drin_E

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/XianHaoMing/article/details/83240162

NN country

Description

n n 个城市形成一棵树的形状。
m m 辆双向班车往返于两个城市(中途经过的城市都会停)。
q q 个人要从城市 x x 到城市 y y ,问最少坐几趟班车。
如果到不了,输出 1 -1

Data Constrints

1 n , m , q 2 1 0 5 1\leq n,m,q \leq 2*10^5

Solution

首先从城市 x x 到城市 y y 的搭车方案分成两类,第一类为在 x x y y l c a lca 下车了,第二类是没有在 l c a lca 处下车。
第一类比较好处理,预处理出 g x , y g_{x,y} 表示从 x x 向上搭 2 y 2^y 趟车最上能去到哪个点,用 g g 数组可以很快的倍增出 x x y y l c a lca 至少要搭多少辆车。

第二类的话先找到 x x y y g g 数组跳到 l c a lca 处的前一个点,记为 x x' y y' ,就是找是否存在一趟车的路覆盖了 x x' y y' ,如果存在,那么答案显然可以少 1 1

至于如何判断的话,实质就是问是否有一条路径一端在 x x' 的子树内,另一端在 y y' 的子树内,算出 d f s dfs 序便变成了一个二维数点问题,但可以不用扫描线处理,具体做法是,考虑离线, d f s dfs 时每遍历到一个点时,找出所有以这个点为一端的车次,并给这些车次的另一端打上加1标记,那么对于一组询问 x x' y y' y y' 子树内的标记和在 x x' 的子树遍历完后发生了变化,就说明存在一条路径一端在 x x' 的子树内,另一端在 y y' 的子树内,由于子树内的 d f s dfs 序是连续的一段,所以询问子树内的标记和用树状数组就可以解决,那这题就做完了。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

#define fo(i,j,l) for(int i=j;i<=l;++i)
#define fd(i,j,l) for(int i=j;i>=l;--i)

using namespace std;
typedef long long ll;
const ll N=51e4,M=N<<2;

int ne[M],lb[M],la[N],dfn[N],en[N],dep[N],fa[N],ans[N];
int ax[N],ay[N];

int f[N][20],g[N][20];
int xx[N],yy[N];
int u1[N],u2[N],v1[N],v2[N],jl[N];

int n,m,x,y,k,op,fs,oo,l,q;
int tr[M];

inline void llb(int a,int b)
{ne[++oo]=la[a]; la[a]=oo; lb[oo]=b;}

inline int min(int a,int b)
{return a<b?a:b;}

inline void dg(int o)
{
	for(int y=la[o];y;y=ne[y]){
		f[lb[y]][0]=o; dep[lb[y]]=dep[o]+1;
		for(l=0;f[f[lb[y]][l]][l];++l)
		f[lb[y]][l+1]=f[f[lb[y]][l]][l];
		dg(lb[y]);
	}
}

inline int get(int a,int b)
{
	if(dep[a]>dep[b])swap(a,b);
	for(int l=19;l>=0;--l)if(dep[f[b][l]]>=dep[a])b=f[b][l];
	for(int l=19;l>=0;--l)if(f[b][l]!=f[a][l])b=f[b][l],a=f[a][l];
	return (a!=b)?fa[a]:a;
}

inline void dfs1(int o)
{
	for(int y=la[o];y;y=ne[y]){
		dfs1(lb[y]);
		if(dep[g[lb[y]][0]]<dep[g[o][0]])g[o][0]=g[lb[y]][0];
		if(dep[g[o][0]]>=dep[o])g[o][0]=n+1;
	}
}

inline void dfs(int o)
{
	dfn[o]=en[o]=++op;
	for(int y=la[o];y;y=ne[y]){
		k=lb[y];
		if(dep[g[k][0]]<dep[k]){
			for(l=0;g[g[k][l]][l]!=n+1;++l)
			g[k][l+1]=g[g[k][l]][l];
		}else g[k][0]=n+1;
		dfs(k); en[o]=en[k];
	}
}

inline int get1(int a,int b)
{
	if(a==b)return 0;
	int aa=0;
	for(int l=19;l>=0;--l)
	if(g[a][l]!=n+1&&dep[g[a][l]]>dep[b])
	a=g[a][l],aa=aa+(1<<l);
	if(g[a][0]!=n+1)aa=aa+1;else aa=n+1;
	return aa;
}

inline int get2(int a,int b)
{
	int aa=0;
	for(int l=19;l>=0;--l)
	if(g[a][l]!=n+1&&dep[g[a][l]]>dep[b])
	a=g[a][l],aa=aa+(1<<l);
	fs=aa; return a;
}

inline void modify(int o,int zl)
{for(;o<=n;o=o+(o&(-o)))tr[o]=tr[o]+zl;}

inline int ask(int o)
{
	int y=0;
	for(;o;o=o-(o&(-o)))y=y+tr[o];
	return y;
}

inline void dfs2(int o)
{
	for(int y=la[o];y;y=ne[y])
	if(lb[y]>2*n){
		k=lb[y]-2*n;
		jl[k]=ask(en[u2[k]])-ask(dfn[u2[k]]-1);
	}
	for(int y=la[o];y;y=ne[y])
	if(lb[y]>n&&lb[y]<=2*n){
		k=lb[y]-n;
		modify(dfn[k],1);
	}
	for(int y=la[o];y;y=ne[y])
	if(lb[y]<=n)dfs2(lb[y]);
	for(int y=la[o];y;y=ne[y])
	if(lb[y]>2*n){
		k=lb[y]-2*n;
		if(ask(en[u2[k]])-ask(dfn[u2[k]]-1)!=jl[k])
		ans[k]=min(ans[k],v1[k]+v2[k]+1);
	}
}

int main()
{
	cin>>n;
	fo(i,2,n){
		scanf("%d",&fa[i]);
		llb(fa[i],i);
	}
	dep[1]=1; dg(1);
	fo(i,1,n)g[i][0]=n+1;
	dep[n+1]=n+1;
	fo(i,1,n)fo(l,0,19)g[i][l]=n+1;
	scanf("%d",&m);
	fo(i,1,m){
		scanf("%d%d",&x,&y);
		if(x==y)continue;
		int z=get(x,y);
		if(dep[z]<dep[g[x][0]])g[x][0]=z;
		if(dep[z]<dep[g[y][0]])g[y][0]=z;
		ax[i]=x; ay[i]=y;
	}
	dfs1(1); 
	dfs(1);
	fo(i,1,m)if(ax[i]!=ay[i])llb(ax[i],ay[i]+n),llb(ay[i],ax[i]+n);
	scanf("%d",&q);
	fo(i,1,q){
		scanf("%d%d",&xx[i],&yy[i]);
		int z=get(xx[i],yy[i]);
		ans[i]=get1(xx[i],z)+get1(yy[i],z);
		u1[i]=get2(xx[i],z); v1[i]=fs;
		u2[i]=get2(yy[i],z); v2[i]=fs;
		if(z!=xx[i]&&z!=yy[i])llb(u1[i],i+n+n);
	}
	dfs2(1);
	fo(i,1,q)if(ans[i]>n)puts("-1");else printf("%d\n",ans[i]); 
}

猜你喜欢

转载自blog.csdn.net/XianHaoMing/article/details/83240162