#樹上啟發式合併,位運算#CF570D Tree Requests
阿新 • • 發佈:2021-07-07
樹上啟發式合併,位運算
題目
給定一個以 \(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; }