1. 程式人生 > 其它 >#樹上啟發式合併,位運算#CF570D Tree Requests

#樹上啟發式合併,位運算#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;
}