A and B and Lecture Rooms [CodeForces-519E]

本文介绍了一种使用树链剖分解决特定树形结构中两点间最短路径平衡点问题的方法。通过分类讨论,文章详细阐述了当两点间距离为奇数或偶数时的不同情况,并给出了相应的算法实现。

题意

有一棵有n个节点的树,每次询问给出两个点x,y,求有多少个点z满足dis(x,z)=dis(y,z)

分析

感觉是分类讨论。一开始想的情况有很多种,而且很复杂。
好在没有直接开始写orz

仔细讨论发现主要需要考虑的只有两种情况。
那么接下来是分类讨论。
1.x,y之间的距离是奇数,那么无解,输出0.
2.如果x==y,那么输出n.

然后是两类需要思考的情况。
首先记离x,y距离相等,且到两点距离最短的点为pos。
那么pos存在的位置可能是

1

pos=lca
pos==lca(x,y)

对于这种情况,答案等于n-x所在子树点的个数-y所在子树点的个数

2

pos!=lca
因为在lca到x或者lca到y上的情况是一样的,所以记较深的点为x。
这里写图片描述
那么显然pos的子树除了包含k的子树之外,点的个数就是贡献。

结束。
实现起来稍微有点麻烦…树链剖分万岁 L(‘ω’)┘三└(‘ω’)」
这里写图片描述

code

#include<bits/stdc++.h>
using namespace std;

void read(int &x){
    x=0; char c=getchar();
    for (;c<48;c=getchar());
    for (;c>47;c=getchar())x=(x<<1)+(x<<3)+(c^48);
}
#define M 100005
struct ed{
    int x,nx;
}e[M<<1];
int nx[M],ecnt,cnt[M],de[M],fa[M],top[M],son[M],tot,dfn[M],id[M];
void add(int x,int y){
    e[ecnt]=(ed){y,nx[x]};
    nx[x]=ecnt++;
}
void dfs1(int f,int x){
    fa[x]=f; cnt[x]=1;;
    if (x!=1)de[x]=de[f]+1;
    son[x]=0;
    for (int i=nx[x];~i;i=e[i].nx)if (e[i].x!=f){
        dfs1(x,e[i].x);
        cnt[x]+=cnt[e[i].x];
        if (cnt[e[i].x]>cnt[son[x]])son[x]=e[i].x;
    }
}
void dfs2(int f,int x){
    top[x]=f; dfn[x]=++tot; id[tot]=x;
    if (son[x])dfs2(f,son[x]);
    for (int i=nx[x];~i;i=e[i].nx)if (e[i].x!=son[x]&&e[i].x!=fa[x]){
        dfs2(e[i].x,e[i].x); 
    }
}
int lca(int x,int y){
    for (;top[x]!=top[y];){
        if (de[top[x]]>de[top[y]])x=fa[top[x]];
        else y=fa[top[y]];
    }
    if (de[x]<de[y])return x; return y;
}
int get(int x,int dep){//x到根的链上,深度为dep的点
    for (;de[top[x]]>dep;){
        x=fa[top[x]];   
    }
    return id[dfn[x]-(de[x]-dep)];
}
void qu(int x,int y){
    if (x==y){
        printf("%d\n",cnt[1]);
        return ;
    }
    int Lca=lca(x,y);
    if ((de[x]+de[y])%2){
        printf("0\n");
        return ;
    }
    if (de[x]==de[y]){
        printf("%d\n",cnt[1]-cnt[get(x,de[Lca]+1)]-cnt[get(y,de[Lca]+1)]);  
        return ;
    }
    else {
        int pos;
        if (de[x]<de[y])swap(x,y);
        pos=get(x,de[x]-(de[x]+de[y]-2*de[Lca])/2);
        printf("%d\n",cnt[pos]-cnt[get(x,de[pos]+1)]);
    }
}
int main(){
//  freopen("1.in","r",stdin);
    int n,x,y,q;
    memset(nx,-1,sizeof(nx));
    read(n);
    for (int i=1;i<n;i++){
        read(x); read(y);
        add(x,y); add(y,x);
    }
    dfs1(1,1);
    dfs2(1,1);
    read(q);
    //printf("done\n");
    for (int i=1;i<=q;i++){
        read(x); read(y);
        qu(x,y);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值