如果题目中只有两个国家,事情就非常简单了:假设只有国家\(A\)\(B\),我们先找出\(A\)\(B\)的最近公共祖先\(lca\),然后找到在路径\(A\longrightarrow lca\longrightarrow B\)上的中点\(mid\),然后分3类讨论:

  1. \(dis(A,mid)==dis(B,mid)\),则\(A\)在这条路径上能到达的范围是\([A,mid)\)\(B\)在这条路径上能到达的范围是\((mid,B]\)。那么\(A\)在全树所能到达的点数为:

    \[\]

    size[son] & \text{if }deep[A]>deep[B] & (son=在路径[mid,A]上的mid的儿子)\
    n-size[mid] & \text{if }deep[B]>deep[A]
    \end{cases}$$

    \(B\)在全树能到达的点数为:

    \[\]

    n-size[mid] & \text{if }deep[A]>deep[B]\
    size[son] & \text{if }deep[B]>deep[A] & (son=在路径[mid,B]上的mid的儿子)
    \end{cases}

    \[ \]

    \[\]

    size[mid] & \text{if }deep[A]>deep[B]\
    n-size[son] & \text{if }deep[B]>deep[A] & (son=在路径[mid,B]上的mid的儿子)
    \end{cases}$$

    \(B\)在全树能到达的点数为:

    \[\]

    n-size[mid] & \text{if }deep[A]>deep[B]\
    size[son] & \text{if }deep[B]>deep[A] & (son=在路径[mid,B]上的mid的儿子)
    \end{cases}

    \[ \]

那么对于三个点的情况,只需对于每个点求出与另外两个点所能到的范围,再取并集就好了。

代码如下:

#include<bits/stdc++.h>
 
#define N 100010
#define LN 18
 
using namespace std;
 
struct data
{
    bool opt;
    int u;
    data(){};
    data(bool a,int b){opt=a,u=b;}
};
 
int t,n,m;
int cnt,head[N],nxt[N<<1],to[N<<1];
int fa[N][LN],d[N],size[N];
 
void init()
{
    cnt=0;
    memset(head,0,sizeof(head));
}
 
void adde(int u,int v)
{
    to[++cnt]=v;
    nxt[cnt]=head[u];
    head[u]=cnt;
}
 
void dfs(int u)
{
    size[u]=1;
    for(int i=1;i<=17;i++)
        fa[u][i]=fa[fa[u][i-1]][i-1];
    for(int i=head[u];i;i=nxt[i])
    {
        if(to[i]!=fa[u][0])
        {
            d[to[i]]=d[u]+1;
            fa[to[i]][0]=u;
            dfs(to[i]);
            size[u]+=size[to[i]];
        }
    }
}
 
int lca(int a,int b)
{
    if(d[a]<d[b])
        swap(a,b);
    for(int i=17;i>=0;i--)
        if(d[fa[a][i]]>=d[b])
            a=fa[a][i];
    if(a==b)
        return a;
    for(int i=17;i>=0;i--)
        if(fa[a][i]!=fa[b][i])
            a=fa[a][i],b=fa[b][i];
    return fa[a][0];
}
 
int up(int u,int d)
{
    for(int i=17;i>=0;i--)
        if(d>=(1<<i))
            u=fa[u][i],d-=(1<<i);
    return u;
}
 
data getmid(int x,int y,int lca)
{
    int len=d[x]+d[y]-(d[lca]<<1);
    if(d[x]>=d[y]) return data(0,up(x,(len-1)>>1));
    return data(1,up(y,len>>1));
}
 
int query(int x,int y,int z,int xy,int xz)
{
    data ans1=getmid(x,y,xy),ans2=getmid(x,z,xz);
    if(!ans1.opt&&!ans2.opt)
    {
        if(d[ans1.u]<d[ans2.u])
            swap(ans1,ans2);
        return size[ans1.u];
    }
    else
    {
        if(ans1.opt&&ans2.opt)
        {
            if(d[ans1.u]<d[ans2.u])
                swap(ans1,ans2);
            if(lca(ans1.u,ans2.u)==ans2.u)
                return n-size[ans2.u];
            return n-size[ans2.u]-size[ans1.u];
        }
        else
        {
            if(ans1.opt)
                swap(ans1,ans2);
            if(lca(ans1.u,ans2.u)==ans1.u)
                return size[ans1.u]-size[ans2.u];
            return size[ans1.u];
        }
    }
}
 
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        init();
        scanf("%d",&n);
        for(int i=1;i<n;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            adde(u,v),adde(v,u);
        }
        d[1]=1;
        dfs(1);
        scanf("%d",&m);
        while(m--)
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            int xy=lca(x,y),xz=lca(x,z),yz=lca(y,z);
            printf("%d %d %d\n",query(x,y,z,xy,xz),query(y,x,z,xy,yz),query(z,x,y,xz,yz));
        }
    }
    return 0;
}

原文地址:http://www.cnblogs.com/ez-lcw/p/16837421.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性