#树上启发式合并,位运算#CF570D Tree Requests

题目

给定一个以 (1) 为根的 (n) 个结点的树,每个点上有一个字母((a-z)),每个点的深度定义为该节点到 (1) 号结点路径上的点数。

每次询问 (a, b) 查询以 (a) 为根的子树内深度为 (b) 的结点上的字母重新排列之后是否能构成回文串。


分析

回文串最多出现一个奇数次字母,考虑26个字母用位运算的异或,
(dp[dep])表示以(1)号点为根深度为(dep)的异或值,
那么这个用树上启发式合并就可以做到(O(nlogn))


代码

#include <cstdio>
#include <cctype>
#include <cstring>
#include <vector>
#define rr register
using namespace std;
const int N=500011;
struct rec{int depth,rk;}; vector<rec>Q[N];
struct node{int y,next;}e[N]; char s[N];
int dep[N],fat[N],root,n,siz[N],Qt,big[N],as[N],dp[N],ans[N];
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline void dfs1(int x,int fa){
	dep[x]=dep[fa]+1,fat[x]=fa,siz[x]=1;
	for (rr int i=as[x],SIZ=-1;i;i=e[i].next){
		dfs1(e[i].y,x),siz[x]+=siz[e[i].y];
		if (siz[e[i].y]>SIZ) big[x]=e[i].y,SIZ=siz[e[i].y];
	}
}
inline bool check(int d){return dp[d]==(-dp[d]&dp[d]);}
inline void update(int x){
    dp[dep[x]]^=1<<(s[x]-97);
	for (rr int i=as[x];i;i=e[i].next)
	if (e[i].y!=fat[x]&&e[i].y!=root)
	    update(e[i].y);
}
inline void dfs2(int x,int opt){
	for (rr int i=as[x];i;i=e[i].next)
	    if (e[i].y!=fat[x]&&e[i].y!=big[x]) dfs2(e[i].y,0);
	if (big[x]) dfs2(big[x],1),root=big[x];
	rr int len=Q[x].size(); update(x);
	for (rr int i=0;i<len;++i)
	    ans[Q[x][i].rk]=check(Q[x][i].depth);
	if (!opt) root=0,update(x);
}
signed main(){
	n=iut(),Qt=iut();
	for (rr int i=2;i<=n;++i){
		rr int x=iut();
		e[i]=(node){i,as[x]},as[x]=i;
	}
	scanf("%s",s+1),dfs1(1,0);
	for (rr int i=1;i<=Qt;++i){
		rr int x=iut(),h=iut();
		Q[x].push_back((rec){h,i});
	}
	dfs2(1,0);
	for (rr int i=1;i<=Qt;++i)
	    puts(ans[i]?"Yes":"No");
	return 0;
}
原文地址:https://www.cnblogs.com/Spare-No-Effort/p/14980652.html