洛谷P5836 [USACO19DEC]Milk Visits S(LCA/并查集)
题目描述
Farmer John 计划建造 NNN 个农场,用 N−1N-1N−1 条道路连接,构成一棵树(也就是说,所有农场之间都互相可以到达,并且没有环)。每个农场有一头奶牛,品种为更赛牛或荷斯坦牛之一。
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-1N−1 行,每行包含两个不同的整数 XXX 和 YYY(1≤X,Y≤N1 \leq X, Y \leq N1≤X,Y≤N),表示农场 XXX 与 YYY 之间有一条道路。
以下 MMM 行,每行包含整数 AiA_iAi,BiB_iBi,以及一个字符 CiC_iCi。AiA_iAi 和 BiB_iBi 表示朋友 iii 拜访时行走的路径的端点,CiC_iCi 是 G
或 H
之一,表示第 iii 个朋友喜欢更赛牛的牛奶或是荷斯坦牛的牛奶。
输出格式
输出一个长为 MMM 的二进制字符串。如果第 iii 个朋友会感到高兴,则字符串的第 iii 个字符为 1
,否则为 0
。
输入输出样例
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
10110蓝色=P1+P2-2*P3+(LCA上的信息)。
LCA做法的话,只需要在BFS预处理时维护两个数组信息:当前点x到树根这条路径上的H和G的数目。查询时,对于两个端点先求LCA,根据朋友喜欢喝H还是G,来求两个端点连的路径上H/G的个数,大于等于1的话朋友就能被满足。至于求法的话盗用一下大佬的图
#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; }