CF 570D(dsu on tree)

题目

n个以1为根的树,每个节点上有一个字母,m次询问,问以x为根的子树中深度为y的节点,用这些节点上的字母来组成一个单词,假如这个字母可以是回文串,则输出Yes,否则输出No

题解

用二进制数存储x节点子树中单词的奇偶,奇为1,偶为0,这是一个26位的二进制数,只要这个二进制数为0或者只有一位为1,那就可以形成回文串
其他的就是dsu on tree 的方法,先dfs一遍,建立每个节点的深度,再dfs一遍,非重儿子计算过后,将二进制数数组删除,重儿子就相反,保存数组

代码

#include<iostream>
#include<vector>
#define ll long long
using namespace std;
const int maxn=6e5+10;
int a[maxn],son[maxn],sz[maxn],head[maxn],dep[maxn],vis[maxn],ans[maxn],cnt;
int h[maxn];
struct E
{
	int to,nxt;
}edge[maxn];
vector<pair<int,int>> que[maxn];
void add_edge(int x,int y)
{
	edge[++cnt].nxt=head[x];
	edge[cnt].to=y;
	head[x]=cnt;
}
bool judge(int m)
{
	if(m==0) return 1;
	else{
		for(int i=0;i<26;i++){
			if(m==(1<<i)) return 1;
		}
		return 0;
	}
}
void dfs(int x,int f)
{
	dep[x]=dep[f]+1;sz[x]=sz[f]+1;
	for(int i=head[x];i;i=edge[i].nxt){
		int v=edge[i].to;
		dfs(v,x);
		sz[x]+=sz[v];
		if(!son[x]||sz[v]>sz[son[x]]) son[x]=v;
	}
}
void cal(int x)
{
	h[dep[x]]^=(1<<a[x]);
	for(int i=head[x];i;i=edge[i].nxt){
		int v=edge[i].to;
		if(!vis[v]) cal(v);
	}
}
void dsu(int x,int p)
{
	for(int i=head[x];i;i=edge[i].nxt){
		int v=edge[i].to;
		if(v!=son[x]) dsu(v,0);
	}
	if(son[x]) dsu(son[x],1),vis[son[x]]=1;
	cal(x);
	for(auto i:que[x]){
		ans[i.first]=judge(h[i.second]);
	}
	if(son[x]) vis[son[x]]=0;
	if(!p) cal(x);
}
int main()
{
	int n,m,x,y;
	scanf("%d%d",&n,&m);
	for(int i=2;i<=n;i++){
		scanf("%d",&x);
		add_edge(x,i);
	}
	char s[maxn];
	scanf("%s", s + 1);
    for (int i = 1; i <= n; i++) a[i] = s[i] - 'a';
    pair<int,int> A;
	for(int i=1;i<=m;i++){
		scanf("%d%d",&x,&y);
		A.first=i;A.second=y;
		que[x].push_back(A);
	}
	dfs(1,0);
	dsu(1,0);
	for(int i=1;i<=m;i++){
		if(ans[i]){
			printf("Yes\n");
		}
		else{
			printf("No\n");
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值