• 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 imes 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:
    ",++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
    ",dep[nowu]+dep[nowv]-2*dep[Lca(nowu,nowv)]+1);
    			}else{
    				printf("0
    ");
    			}
    		}
    	}
    }
    
    越自律,越自由
  • 相关阅读:
    【STL源码学习】STL算法学习之二
    android_SurfaceView 画图
    android_layout_linearlayout(一)
    LINUX_记录(一)
    工作经验之石氏thinking
    android_layout_linearlayout(二)
    android_layout_relativelayout(一)
    两个线程解决一个线程卡之路
    android_layout_relativelayout(二)
    android_layout_framelayout
  • 原文地址:https://www.cnblogs.com/ha-chuochuo/p/14545969.html
Copyright © 2020-2023  润新知