洛谷P5836 [USACO19DEC]Milk Visits S(LCA/并查集)

题目描述

Farmer John 计划建造 NNN 个农场,用 N−1N-1N1 条道路连接,构成一棵树(也就是说,所有农场之间都互相可以到达,并且没有环)。每个农场有一头奶牛,品种为更赛牛或荷斯坦牛之一。

Farmer John 的 MMM 个朋友经常前来拜访他。在朋友 iii 拜访之时,Farmer John 会与他的朋友沿着从农场 AiA_iAi 到农场 BiB_iBi 之间的唯一路径行走(可能有 Ai=BiA_i = B_iAi=Bi)。除此之外,他们还可以品尝他们经过的路径上任意一头奶牛的牛奶。由于 Farmer John 的朋友们大多数也是农场主,他们对牛奶有着极强的偏好。他的有些朋友只喝更赛牛的牛奶,其余的只喝荷斯坦牛的牛奶。任何 Farmer John 的朋友只有在他们访问时能喝到他们偏好的牛奶才会高兴。

请求出每个朋友在拜访过后是否会高兴。

输入格式

输入的第一行包含两个整数 NNN 和 MMM。

第二行包含一个长为 NNN 的字符串。如果第 iii 个农场中的奶牛是更赛牛,则字符串中第 iii 个字符为 G,如果第 iii 个农场中的奶牛是荷斯坦牛则为 H

以下 N−1N-1N1 行,每行包含两个不同的整数 XXX 和 YYY(1≤X,Y≤N1 leq X, Y leq N1X,YN),表示农场 XXX 与 YYY 之间有一条道路。

以下 MMM 行,每行包含整数 AiA_iAiBiB_iBi,以及一个字符 CiC_iCiAiA_iAiBiB_iBi 表示朋友 iii 拜访时行走的路径的端点,CiC_iCiGH 之一,表示第 iii 个朋友喜欢更赛牛的牛奶或是荷斯坦牛的牛奶。

输出格式

输出一个长为 MMM 的二进制字符串。如果第 iii 个朋友会感到高兴,则字符串的第 iii 个字符为 1,否则为 0

输入输出样例

输入 #1
5 5
HHGHG
1 2
2 3
2 4
1 5
1 4 H
1 4 G
1 3 G
1 3 H
5 5 H
输出 #1
10110
LCA做法的话,只需要在BFS预处理时维护两个数组信息:当前点x到树根这条路径上的H和G的数目。查询时,对于两个端点先求LCA,根据朋友喜欢喝H还是G,来求两个端点连的路径上H/G的个数,大于等于1的话朋友就能被满足。至于求法的话盗用一下大佬的图
蓝色=P1+P2-2*P3+(LCA上的信息)。

看题解区还有一种并查集做法,“这一棵树只有两种颜色,故我们只要记录树上一个个颜色相同的连通块,只有当所查询两点是同一连通块且连通块颜色与目标颜色不同时输出0。”https://www.luogu.com.cn/problemnew/solution/P5836
%%%orz蛮好一道题
#include <bits/stdc++.h>
#define N 100005 
using namespace std;
int n,m,t,tot=0,head[N],ver[2*N],edge[2*N],Next[2*N],f[N][22],d[N],dist[N],lg[100005];
char s[100005];
int h[500005],g[500005];//h[i]表示树根到i共有多少H g[i]同理 
void add(int x,int y,int z)
{
    ver[++tot]=y,edge[tot]=z,Next[tot]=head[x],head[x]=tot;
}
void prework()
{
    queue<int>q;
    q.push(1);
    d[1]=1;
    if(s[0]=='H')
    {
        h[1]=1;
    }
    else g[1]=1;
    while(q.size())
    {
        int x=q.front();
        q.pop();
        int i;
        for(i=head[x];i;i=Next[i])
        {
            int y=ver[i],z=edge[i];
            if(d[y])continue;
            d[y]=d[x]+1;
            dist[y]=dist[x]+z;
            f[y][0]=x;//y节点的2^0倍祖先是x
            if(s[y-1]=='H')
            {
                h[y]=h[x]+1;
                g[y]=g[x];//别漏下!!! 
            }
            else
            {
                g[y]=g[x]+1;
                h[y]=h[x];
            }
            int j;
            for(j=1;j<=lg[d[x]];j++)
            {
                f[y][j]=f[f[y][j-1]][j-1];
            }
            q.push(y);
            
        }
    }
}
int lca(int x,int y)
{
    if(d[x]<d[y])swap(x,y);//使得x深度较小 
    int i;
    while(d[x] > d[y])
    {
        x = f[x][lg[d[x]-d[y]] - 1];
    }
    if(x==y)return x;
    for(i=lg[d[x]] - 1;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>>m;
    int i;
    memset(d,0,sizeof(d));
    scanf("%s",s);
    for(i=1;i<=n-1;i++)
    {
        int x,y;
        cin>>x>>y;
        add(x,y,1);
        add(y,x,1);
    }
     for(int i = 1; i <= n+1; ++i)
        lg[i] = lg[i-1] + (1 << lg[i-1] == i);
    prework();
    vector<int>v; 
    for(i=1;i<=m;i++)
    {
        int a,b;
        char c;//
        scanf("%d%d",&a,&b);
         scanf(" %c",&c);///!!!!!!一定要这么写 
        int LCA=lca(a,b);
        if(c=='H')
        {
            if((h[a]+h[b]-2*h[LCA]+(s[LCA-1]=='H'?1:0))>0)v.push_back(1);
            else v.push_back(0);
        }
        else
        {
            if((g[a]+g[b]-2*g[LCA]+(s[LCA-1]=='G'?1:0))>0)v.push_back(1);
            else v.push_back(0);
        }
    }
    for(i=0;i<v.size();i++)cout<<v[i];
    return 0;
}
原文地址:https://www.cnblogs.com/lipoicyclic/p/12610009.html