NKOJ P3815 树上的询问 (LCA 倍增)(复习距离倍增)

评测说明 : 1000ms
问题描述

现有一棵 n 个节点的树,树上每条边的长度均为 1。给出 m 个询问,每次询问两个节 点 x,y,求树上到 x,y 两个点距离相同的节点数量。

输入格式

第一个整数 n,表示树有 n 个点。
接下来 n-1 行每行两整数 a,b,表示从 a 到 b 有一条边。
接下来一行一个整数 m,表示有 m 个询问。
接下来 m 行每行两整数 x,y,询问到 x 和 y 距离相同的点的数量。

输出格式

共 m 行,每行一个整数表示询问的答案。

样例输入


1 2 
1 3 
2 4 
2 5 
3 6 
3 7 

1 2 
4 5
2 3

思路: 被坑了好久 一直没调出来  复习了一波LCA 后 终于A 了 注意 倍增的用法 即GOUP 操作

顺便复习一下 距离倍增

code:

g[x][i]=g[x][i-1]+g[fa[x][i-1]][i-1];

dis 求法

for(i=0;i<=ss;i++)
    if(k&(1<<i))dis+=g[x][i],x=fa[x][i];
if(x==y)return dis;
for(i=s;i>=0;i--)
        if(fa[x][i]!=fa[y][i])
        {
            dis+=g[x][i];x=fa[x][i];

            dis+=g[y][i];y=fa[y][i];
        }
    return dis+=g[x][0]+g[y][0];

code:

//
#include<bits/stdc++.h>
using namespace std;
#define maxnn 600000
int f[maxnn][100];
int las[maxnn],nex[maxnn],en[maxnn],tot;
int n,m;
int dep[maxnn];
int x,y;
int size[maxnn];
void dfs(int v,int fa)
{
    dep[v]=dep[fa]+1;
    f[v][0]=fa;
    int s=ceil(log2(n));
    for(int i=1;i<=s;i++)
    {
        f[v][i]=f[f[v][i-1]][i-1];
    }
    for(int i=las[v];i;i=nex[i])
    {
        int u=en[i];
        if(u!=fa)
        {
            dfs(u,v);
            size[v]+=size[u];
        }
    }
    return ;
}
void add(int a,int b)
{
    en[++tot]=b;
    nex[tot]=las[a];
    las[a]=tot;
}
int goup(int x,int s)
{
    int k=ceil(log2(n));
    for(int i=0;i<=k;i++)
    {
        if((s&(1<<i)))
        {
            x=f[x][i];
        }
    }
    return x;
}
int lca(int x,int y)
{
    if(dep[x]<dep[y])swap(x,y);
    x=goup(x,dep[x]-dep[y]);
    if(x==y) return y;
    int s=ceil(log2(dep[x]));
    for(int i=s;i>=0;i--)
    {
        if(f[x][i]!=f[y][i])
        {
            x=f[x][i];
            y=f[y][i];
        }
    }
    return f[x][0];
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) size[i]=1;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    dfs(1,0);
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        int k1=0,k=0;
        scanf("%d%d",&x,&y);
        if(y==x) {cout<<n<<endl;continue;}
        if(dep[x]==dep[y])
        {
            int k=dep[x]-dep[lca(x,y)];
            int k1=n-size[goup(x,k-1)]-size[goup(y,k-1)];
            cout<<k1<<endl;
            continue;
        }
        if((abs((dep[x]-dep[y])))&1) {cout<<0<<endl;continue;}
        if(dep[x]<dep[y])
        swap(x,y);
        k=(dep[y]+dep[x]-2*dep[lca(x,y)])/2;
        int p1=goup(x,k-1);int p2=goup(x,k);
        k1=size[p2]-size[p1];
        cout<<k1<<endl;

    }
 } 

原文地址:https://www.cnblogs.com/OIEREDSION/p/11324624.html

时间: 2024-10-13 21:41:54

NKOJ P3815 树上的询问 (LCA 倍增)(复习距离倍增)的相关文章

【BZOJ1316】树上的询问 点分治+set

[BZOJ1316]树上的询问 Description 一棵n个点的带权有根树,有p个询问,每次询问树中是否存在一条长度为Len的路径,如果是,输出Yes否输出No. Input 第一行两个整数n, p分别表示点的个数和询问的个数. 接下来n-1行每行三个数x, y, c,表示有一条树边x→y,长度为c. 接下来p行每行一个数Len,表示询问树中是否存在一条长度为Len的路径. Output 输出有p行,Yes或No. Sample Input 6 4 1 2 5 1 3 7 1 4 1 3 5

[bzoj1316]树上的询问_点分治

树上的询问 bzoj-1316 题目大意:一棵n个点的带权有根树,有p个询问,每次询问树中是否存在一条长度为Len的路径,如果是,输出Yes否输出No. 注释:$1\le n\le 10^4$,$1\le p\le 100$,长度$\le 10^6$. 想法:有根树tm是啥意思?根在jb哪呢?老子我瞅tm这么半天也没看见根在哪呢??这题点分治即可.我们用点分治的第二种:分别计算子树,然后用之前的信息更新答案.对于此题,我们可以直接维护一个set就行. 最后,附上丑陋的代码... ... #inc

树,LCA,最近公共祖先,倍增

最近公共祖先, 树上倍增,LCA, fa [ i ] [ j ] 表示 i 节点向上 2j 的祖先 很像dp, k 属于 [ 1 , log ( n ) ] ,f [ x ][ k ] = f [ f [ x ] [ k-1 ]] [k-1] 算lca时, 先不妨设 d [ x ] >= d [ y ] 二进制拆分 尝试从x 向上走 k = 2log (n),~~,21,20, 检查到的点是否比 y 深 每次检查中,若是,x= f [ x ] [ k ] 若仍未相会, 即 f [ x ] [ k

[csu/coj 1079]树上路径查询 LCA

题意:询问树上从u到v的路径是否经过k 思路:把树dfs转化为有根树后,对于u,v的路径而言,设p为u,v的最近公共祖先,u到v的路径必定是可以看成两条路径的组合,u->p,v->p,这样一来便可以将判断条件转化为(LCA(u,k)=k  || LCA(v,k)=k) && LCA(k,p)=p.由于这个LCA询问里面需要用到中间结果p,所以这种方法用tarjan离线不行,只能用dfs+RMQ. 1 #pragma comment(linker, "/STACK:10

LCA算法解析-Tarjan&amp;倍增

LCA_Tarjan LCA的Tarjan算法的时间复杂度为O(n+q)是一种离线算法,要用到并查集.Tarjan算法基于dfs,在dfs的过程中,对于每个节点位置的询问做出相应的回答.dfs的过程中,当一棵子树被搜索完成之后,就把他和他的父亲合并成同一集合:在搜索当前子树节点的询问时,如果该询问的另一个节点已经被访问过,那么该编号的询问是被标记了的,于是直接输出当前状态下,另一个节点所在的并查集的祖先:如果另一个节点还没有被访问过,那么就做下标记,继续dfs.当然,暂时还没那么容易弄懂,所以建

SPOJ COT Count on a tree(树上主席树 + LCA 求路径第k小)题解

题意:n个点的树,每个点有权值,问你u~v路径第k小的点的权值是? 思路: 树上主席树就是每个点建一棵权值线段树,具体看JQ博客,LCA用倍增logn求出,具体原理看这里 树上主席树我每个点的存的是点u到源点1的权值线段树,那我求点u到v的所有点,显然是 u + v - lca - fa[lca],就是u到1 + v到1 - 多算的lca - 多算的fa[lca].不能减去两个lca不然少一个点了, LCA板子: //LCA int fa[maxn][20]; int dep[maxn]; vo

cqyz oj | 树上的询问 | 最近公共祖先

Description 现有一棵 n 个节点的棵, 树上每条边的长度均为 1. 给出 m 个询问, 每次询问两个节点 x,y, 求树上到 x,y 两个点距离相同的节点数量. Input 第一个整数 n, 表示树有 n 个点. 接下来 n-1 行每行两整数 a, b, 表示从 a 到 b 有一条边. 接下来一行一个整数 m, 表示有 m 个询问. 接下来 m 行每行两整数 x, y, 询问到 x 和 y 距离相同的点的数量. Output 共 m 行, 每行一个整数表示询问的答案. Sample

POJ1679 The Unique MST 题解 次小生成树 题解 Kruskal+暴力LCA解法(因为倍增不会写)

题目链接:http://poj.org/problem?id=1679 题目大意: 给你一个简单连通图,判断他的最小生成树是否唯一. 解题思路: 首先(我这里用Kruskal算法)求出它的最小生成树(以下简称MST)以及对应的边,然后构造出这棵MST. 然后我们枚举图上每一条不在此MST上的边,假设这条边的两个端点是 \(u\) 和 \(v\),边权为 \(w\) ,求MST上 \(u\) 到 \(v\) 的树链上的最大边的值是否等于 \(w\),如果等于 \(w\) 就说明 MST 不唯一.

【bzoj3251】树上三角形 朴素LCA+暴力

题目描述 给定一大小为n的有点权树,每次询问一对点(u,v),问是否能在u到v的简单路径上取三个点权,以这三个权值为边长构成一个三角形.同时还支持单点修改. 输入 第一行两个整数n.q表示树的点数和操作数 第二行n个整数表示n个点的点权 以下n-1行,每行2个整数a.b,表示a是b的父亲(以1为根的情况下) 以下q行,每行3个整数t.a.b 若t=0,则询问(a,b) 若t=1,则将点a的点权修改为b 输出 对每个询问输出一行表示答案,“Y”表示有解,“N”表示无解. 样例输入 5 5 1 2