这道题我写了两种解法,看到这道题我的并查集DNA一下子就动了,心想这不就是一个并查集吗,将相同种类的相连子块合并在一起,查询的时候若两个id的父节点不是一个,则证明两个id的路径横跨'H'和'U'两个子块,若两个父节点是一个,则需要查验是不是要求的种类即可。
并查集代码如下:(近乎于O(n))
#include <iostream>
#include <vector>
using namespace std;
const int N=1e5+10;
int g[N],fa[N],kin[N];
vector<int>adj[N];
int n,m,x,y;
char c;
void dfs(int s,int u)
{
if(g[s]==g[u]) fa[u]=s;
else kin[u]=g[u];
for(auto v:adj[u])
{
if(v==s)continue;
dfs(u,v);
}
}
int find(int x)
{
if(x!=fa[x]) fa[x]=find(fa[x]);
return fa[x];
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>c;
if(c=='H') g[i]=1;
fa[i]=i;
}
for(int i=1;i<n;i++)
{
cin>>x>>y;
adj[x].push_back(y);
adj[y].push_back(x);
}
g[0]=-1;
dfs(0,1);
while(m--)
{
int t=0;
cin>>x>>y>>c;
if(c=='H') t=1;
x=find(x),y=find(y);
if(x==y&&kin[x]!=t) cout<<0;
else cout<<1;
}
return 0;
}
第二种是我认为尽管可以并查集,但我们还是要写一下最近公共祖先(LCA)毕竟最近在学这个,
用LCA时,我们可以只维护一个节点到根节点路径上H和G的数量就行。
LCA代码如下:(复杂度O(n+mlogn ))
#include <iostream>
#include <vector>
using namespace std;
const int N=1e5+10;
vector<int>adj[N];
int fa[N][20],lg[N],H[N],G[N],depth[N];
char g[N];
int n,m;
void init()
{
for(int i=1;i<=n;i++)
lg[i]=lg[i-1]+(1<<lg[i-1]==i);
for(int i=1;i<=n;i++)lg[i]--;
}
void dfs(int f,int u)
{
depth[u]=depth[f]+1;
H[u]=H[f]+(g[u]=='H');
G[u]=G[f]+(g[u]=='G');
int d=depth[u];
fa[u][0]=f;
for(int i=1;i<=lg[d];i++)
{
fa[u][i]=fa[fa[u][i-1]][i-1];
}
for(auto v:adj[u])
{
if(v==f)continue;
dfs(u,v);
}
}
int lca(int x,int y)
{
if(depth[x]<depth[y])swap(x,y);
int u=y,v=x;
while(depth[x]>depth[y])
x=fa[x][lg[depth[x]-depth[y]]];
if(x==y)return x;
for(int i=lg[depth[x]];i>=0;i--)
if(fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>g[i];
for(int i=1;i<n;i++)
{
int x,y;
cin>>x>>y;
adj[x].push_back(y);
adj[y].push_back(x);
}
init();
dfs(0,1);
while(m--)
{
char c;
int x,y;
cin>>x>>y>>c;
int u=lca(x,y);
int th,tg;
th=H[x]+H[y]-2*H[u]+(g[u]=='H');
tg=G[x]+G[y]-2*G[u]+(g[u]=='G');
if(c=='H'&&th||c=='G'&&tg)cout<<1;
else cout<<0;
}
return 0;
}