Milk Visits
题目大意:
有n个农场,这些农场由n-1条边相连【构成一棵树】,每个节点都会有一个权值 G或H,M次询问,每次输入x,y,ch,输出从x到y过程中能否遇见权值为ch的农场
题目思路:
没做过多少LCA的应用,至于这个题也没有看出来,其实LCA的作用在于求树上两点的距离,可以运用LCA求出树上两点最近路径的权值之和。
所以这个题考察的就是LCA求两点路径权值之和,只不过点权变为了边权,首先我们需要把问题转换一下,我们令G为1,H为-1,那么问题就转换成为了,如果两点之间路径点权之和==(路径长度+1),代表这条路全为G,路径点权之和==(路径长度+1)的相反数,代表这条路全为H,在这两者之间的话既有G又有H。所以只需要在LCA的时候不仅维护deep数组,再维护一个点权数组就可以了。
最后判断一下,路径的点权和与路径上点数的关系,就可以得到答案。
AC:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF=1000000000000005;
const ll maxn=5e5+5;
const int mod=998244353;
ll n,m;
int deep[maxn];//树的深度
int f[maxn][30];//fa[i][j] 节点i的2^j次方的祖先
struct node{
int e,next;
}edge[maxn];
ll cnt=0;
int head[maxn];
int dis[maxn];
char str[maxn];
void addedge(int u,int v)
{
edge[cnt]=node{v,head[u]};
head[u]=cnt++;
}
void dfs(int u,int fa,int z)//预处理深度 与祖先的关系
{
deep[u]=deep[fa]+1;
dis[u]=dis[fa]+z;
f[u][0]=fa;
for(int i=1;(1<<i)<=deep[u];i++)//不加限制也行
f[u][i]=f[f[u][i-1]][i-1];//2^i = 2^(i-1)+2^(i-1)
for(int i=head[u];~i;i=edge[i].next){
int e=edge[i].e;
int w;
if(e!=fa) dfs(e,u,(str[e]=='G'?1:-1));
}
}
int LCA(int u,int v)
{
if(deep[u]<deep[v]) swap(u,v);//保持u比v深
for(int i=25;i>=0;i--) if(deep[f[u][i]]>=deep[v]) u=f[u][i];//比他深往上跳
if(u==v) return u;
for(int i=25;i>=0;i--){
if(f[u][i]!=f[v][i]){//因为从同一深度开始向上跳 一样有可能是更远的祖先
u=f[u][i];
v=f[v][i];
}
}
return f[u][0];//随便返回一个上一级即可
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%lld%lld",&n,&m);
scanf("%s",str+1);
for(int i=1;i<=n-1;i++)
{
ll x,y;scanf("%lld%lld",&x,&y);
addedge(x,y);
addedge(y,x);
}
int tempx=(str[1]=='G'?1:-1);
dfs(1,1,tempx);
for(int i=1;i<=m;i++)
{
ll x,y;
char op[5];
scanf("%lld%lld%s",&x,&y,op);
int amit=LCA(x,y);
int dis1=deep[x]+deep[y]-2*deep[amit]+1;
int dis2=dis[x]+dis[y]-2*dis[amit]+(str[amit]=='G'?1:-1);
if(dis2>-dis1&&dis2<dis1) printf("1");
else if(dis2==dis1&&op[0]=='G') printf("1");
else if(dis2==dis1&&op[0]=='H') printf("0");
else if(dis2==-dis1&&op[0]=='G') printf("0");
else if(dis2==-dis1&&op[0]=='H') printf("1");
}
printf("\n");
return 0;
}
/**
***/