• LCA【SP913】Qtree


    Description

    给定一棵n个点的树,边具有边权。要求作以下操作:

    DIST a b 询问点a至点b路径上的边权之和

    KTH a b k 询问点a至点b有向路径上的第k个点的编号

    有多组测试数据,每组数据以DONE结尾。

    Input

    第一组数据包含一个整数(T),代表有(T)组测试数据。(1leq T leq 25)

    对每一组测试数据:

    • 第一行一个整数(N(n leq 10000))
    • 接下来有(N-1)行,每一行描述树上的一条边(a,b,c( cleq 100000))
    • 接下来几行操作包括(DIST a b),(KTH a b k)
    • (DONE)结尾

    Output

    对于每一个(DIST)(KTH)询问输出一行.

    很明显,LCA,但是难点就在于如何求出(KTH)对于的答案.

    首先会存在两种情况

    一. (k leq depth[x]-depth[lca_{x,y}]+1)

    很明显,这时第(k)个点必然在于(x->lca_{x,y})的路径上,我们只需要知道其深度即可倍增求取.

    可求其深度为(depth[x]-k+1)

    二. (k > depth[x]-depth[lca_{x,y}]+1)

    这时,第(k)个点必然存在于(y->lca_{x,y})的路径上,但是如何求其深度却是一个问题.

    先设(ans)为第(k)个点的深度.

    我们可以得到的信息是(k)必须要在(y->lca_{x,y})

    所以新的深度至少必须为(k-(depth[x]-depth[lca_{x,y}]+1))

    但是由于我们的(lca_{x,y})不一定为(1)(这里我以(1)为根)

    所以原式子还需要加上一个(depth[lca_{x,y}])

    因此可以得到这样一个式子

    [ans=k-depth[x]+2*depth[lca_{x,y}]-1; ]

    知道深度之后,直接倍增跳即可.

    代码

    #include<cstdio>
    #include<cctype>
    #include<cstring>
    #include<algorithm>
    #define clear(a) memset(a,0,sizeof a)
    #define N 10008
    #define R register
    using namespace std;
    inline void in(int &x)
    {
    	int f=1;x=0;char s=getchar();
    	while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    	while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    	x*=f;
    }
    int T;
    int n,head[N],tot;
    struct cod{int u,v,w;}edge[N<<2];
    inline void add(int x,int y,int z)
    {
    	edge[++tot].u=head[x];
    	edge[tot].v=y;
    	edge[tot].w=z;
    	head[x]=tot;
    }
    int depth[N],f[N][21],dis[N];
    void dfs(int u,int fa,int dist)
    {
    	f[u][0]=fa;dis[u]=dis[fa]+dist;depth[u]=depth[fa]+1;
    	for(R int i=1;(1<<i)<=depth[u];i++)
    		f[u][i]=f[f[u][i-1]][i-1];
    	for(R int i=head[u];i;i=edge[i].u)
    	{
    		if(edge[i].v==fa)continue;
    		dfs(edge[i].v,u,edge[i].w);
    	}
    }
    inline int lca(int x,int y)
    {
    	if(depth[x]>depth[y])swap(x,y);
    	for(R int i=17;i>=0;i--)
    		if(depth[x]+(1<<i)<=depth[y])
    			y=f[y][i];
    	if(x==y)return y;
    	for(R int i=17;i>=0;i--)
    	{
    		if(f[x][i]==f[y][i])continue;
    		x=f[x][i],y=f[y][i];
    	}
    	return f[x][0];
    }
    char s[108];
    inline int query(int x,int y,int k)
    {
    	R int la=lca(x,y);
    	if(depth[x]-depth[la]+1>=k)
    	{
    		R int ans=depth[x]-k+1;
    		for(R int i=17;i>=0;i--)
    		{
    			if(depth[x]-ans>=(1<<i))
    				x=f[x][i];
    		}
    		return x;
    	}
    	else
    	{
    		R int ans=depth[la]*2+k-depth[x]-1;
    		for(R int i=17;i>=0;i--)
    			if((1<<i)<=depth[y]-ans)
    				y=f[y][i];
    		return y;
    	}
    }
    int main()
    {
    	in(T);
    	for(;T;T--)
    	{
    		in(n);
    		tot=0;clear(head),clear(dis),clear(f);clear(depth);
    		for(R int i=1,x,y,z;i<n;i++)
    		{
    			in(x),in(y),in(z);
    			add(x,y,z);add(y,x,z);
    		}
    		dfs(1,0,0);
    		for(R int x,y,la,k;;)
    		{
    			scanf("%s",s+1);
    			if(s[2]=='O')break;
    			if(s[2]=='I')
    			{
    				in(x),in(y);
    				la=lca(x,y);
    				printf("%d
    ",dis[x]+dis[y]-2*dis[la]);
    			}
    			else
    			{
    				in(x),in(y),in(k);
    				printf("%d
    ",query(x,y,k));
    			}
    		}
    	}
    }
    
  • 相关阅读:
    sh_02_判断年龄改进版
    面向对象-内置函数和内置方法
    python面向对象-三大特性
    面向对象编程
    面向对象编程-回合制游戏
    练习Dream-购物车
    bytes和str的区别与转换
    字符串的操作和使用方法。
    程序员必须掌握的600个单词
    移动端的网页设计流程有哪些?
  • 原文地址:https://www.cnblogs.com/-guz/p/9822881.html
Copyright © 2020-2023  润新知