Path Intersection(Gym102040F)(lca)

题意:

题目给出一棵树,然后给出 \(K\) 对点,每对点表示在树上的一条简单路径,问这 \(K\) 对点共同覆盖的节点有几个。

想法:

  • 考虑树上 \(lca\) 的一个性质:树上两条路径的交点,只存在这两条路径的起始点的两两之间的 \(lca\) 的深度最深的两个点所构成的路径。如 \((u_{1},v_{1})\)\((u_{2},v_{2})\) 两条路径的交点只存在于 \(lca(u_{1},u_{2})\), \(lca(u_{1},v_{2})\), \(lca(u_{2},v_{1})\), \(lca(u_{2},v_{2})\) 这四个点中深度最深的两个点所构成的路径上。
  • 那么我们就可以通过把 \(K\) 对点进行遍历,就可以求出公共路径的起始点。
  • 已知起始点,那么利用性质:树上两点间的距离是 \(deep[u]+deep[v]-2\times deep[lca(u,v)]\)

代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e4+10;

inline int rd() 
{
    int res = 0,flag = 0;
    char ch;
    if ((ch = getchar()) == '-')flag = 1;
    else if(ch >= '0' && ch <= '9')res = ch - '0';
    while ((ch = getchar()) >= '0' && ch <= '9')res = (res<<1) + (res<<3) + (ch - '0');
    return flag ? -res : res;
}
int n;
int head[2*MAXN]; 
int num=0;
struct edg{
   int next,to;
}edge[2*MAXN];
void edge_add(int u,int v)   //链式前向星存图
{
   num++;
   edge[num].next=head[u];edge[num].to=v;head[u]=num;
   edge[++num].next=head[v];edge[num].to=u;head[v]=num;
}
//---------------------
// lca部分
int dep[MAXN]={0},f[MAXN][23];
void init()
{
	for(int i=0;i<2*MAXN;i++){
		head[i]=0;
		if(i<MAXN){
			dep[i]=0;
			for(int j=0;j<23;j++)f[i][j]=0;
		}
	}
	num=0;
}
void dfs(int u,int father)//对应深搜预处理f数组 
{
    dep[u]=dep[father]+1;
    for(int i=1;(1<<i)<=dep[u];i++)
    {
        f[u][i]=f[f[u][i-1]][i-1];
    }
    for(int i=head[u];i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==father)continue;//双向图需要判断是不是父亲节点 
        f[v][0]=u;
        dfs(v,u);
    }
}
int Lca(int x,int y)
{
    if(dep[x]<dep[y])swap(x,y);
    for(int i=20;i>=0;i--)//从大到小枚举使x和y到了同一层 
    {
        if(dep[f[x][i]]>=dep[y])x=f[x][i];
        if(x==y)return x;
    }
    for(int i=20;i>=0;i--)//从大到小枚举 
    {
        if(f[x][i]!=f[y][i])//尽可能接近 
        {
            x=f[x][i];y=f[y][i];
        } 
    } 
    return f[x][0];//随便找一个**输出 
}

//

bool cmp(int x,int y){
	return dep[x]>dep[y];
}
/*
int ans;
void dfs2(int u,int fa,int v,int length)
{
	if(u==v){
		ans=length;
		return;
	}
	for(int i=head[u];i!=0;i=edge[i].next){
		if(edge[i].to==fa)continue;
		dfs2(edge[i].to,u,v,length+1);
	}
	//cout<<sum<<endl;
}
*/
int main()
{
	int T,CASE=0;
	cin>>T;
	while(T--){
		printf("Case %d:\n",++CASE);
		init();
		n=rd();
		for(int i=1;i<n;i++){
			int u,v;
			u=rd(),v=rd();
			edge_add(u,v);
		}
		dfs(1,0);
		int q;
		q=rd();
		while(q--){
			int K;
			int nowu,nowv;
			K=rd();
			int KK=1;
			for(int i=1;i<=K;i++){
				int u,v;
				u=rd(),v=rd();
				if(i==1){
					nowu=u;
					nowv=v;
					continue;
				}
				int p[5];
				p[1]=Lca(nowu,u);
				p[2]=Lca(nowu,v);
				p[3]=Lca(nowv,u);
				p[4]=Lca(nowv,v);
				sort(p+1,p+5,cmp);
				//cout<<p[1]<<" "<<p[2]<<" "<<p[3]<<" "<<p[4]<<endl;
				if(p[1]!=p[2]){
					nowu=p[1];
					nowv=p[2];
				}else{
					if(dep[p[1]]<dep[Lca(nowu,nowv)]||dep[p[1]]<dep[Lca(u,v)])KK=0;
					nowu=p[1];
					nowv=p[1];
				}
				//cout<<nowv<<endl;
			}
			//cout<<"xxx"<<KK<<" "<<nowu<<" "<<nowv<<endl;
			if(KK){
				printf("%d\n",dep[nowu]+dep[nowv]-2*dep[Lca(nowu,nowv)]+1);
			}else{
				printf("0\n");
			}
		}
	}
}
posted @ 2021-03-16 21:16  hachuochuo  阅读(71)  评论(0编辑  收藏  举报