【HDU6647】Bracket Sequences on Tree(树Hash 树上Dp)

题目链接

大意

给出一颗树,按下列方式生成一个括号序列。

function dfs(int cur, int parent):
  print('(')
  for all nxt that cur is adjacent to:
    dfs(nxt, cur)
  print(')')

其中可以从任一点出发,且对儿子的遍历顺序是随机的。
求本质不同的括号序列个数。

思路

前置板块:树Hash

如何判断两颗有根树是否本质一样?
我们先随机生成一个\(T\)数组(随机数被卡概率小?)
\(Siz[u]\)表示\(u\)的子树大小,\(H[u]\)表示以\(u\)为根的有根子树的Hash值。
那么\(H[u]\)的定义式就为:$$H[u]=C+T[Siz[u]]\times H[v]~~(v\in Son[u])$$其中\(C\)为随机构造的一个数。(主要是怕被卡)
若对于某对\(i,j\),有\(H[i]=H[j]\),则说明以\(i\)为根的有根树的形态与以\(j\)为根是一样的。

那么如何判断无根树呢?
我们采用换根Dp来做,考虑\(H\)数组的定义式。
那么向下转移时就有:$$H[v]=H[v]+T[Siz[u]-Siz[v]]\times(H[u]-H[v]\times T[Siz[v]])~~(v\in Son[u])$$这样,我们就可以得到以每个点为根的有根树的Hash值了。
考虑整棵无根树,我们取以每个点为根的有根树的最大Hash值就行了,即\(Max(H[i])\).


考虑固定了起点的情况。
那么对于两颗形状不同的有根子树,其所生成的括号序列集是显然没有交集的。
则对于某个点来说,我们需要记录它本质相同的儿子子树的个数。
我们设\(Cnt[u][x]\)表示\(u\)的儿子中Hash值为\(x\)的的个数。(Map好啊)
\(u\)为根的子树内的答案\(Dp[u]\)就为

\[Dp[u]=\frac{Son[u]!}{\prod Cnt[u][x]!}\times Dp[v]~~(x\in H[v])$$其中$Son[u]$表示$u$儿子的个数。 对于起点不固定的情况。 其实我们换根Dp就行了,细节有点多,注意一下。 最后我们统计答案时,每种Hash值只统计一次就行了。 # 代码 及其丑陋的代码。 可恶的出题人卡简单Hash。 ```cpp #include<map> #include<ctime> #include<cstdio> #include<vector> #include<algorithm> using namespace std; #define ULL unsigned long long const int C=10007; const int MAXT=100000; const int MAXN=100005; const int MOD=998244353; const ULL LW=2305843009213693951ll; ULL Ans; ULL T[MAXN],H[MAXN]; int K,N,Siz[MAXN],Son[MAXN]; ULL Dp[MAXN]; ULL E[MAXN],F[MAXN]; vector<int>P[MAXN]; vector<ULL>Kind[MAXN]; map<ULL,int>Mp; map<ULL,int>Cnt[MAXN]; ULL Mul(ULL X,ULL Y){ return (X*Y-(ULL)(X/(long double)LW*Y+1e-3)*LW+LW)%LW; } ULL quick_Pow(ULL x,ULL y){ if(y==0)return 1; if(y==1)return x; if(y%2)return (x*quick_Pow((x*x)%MOD,y/2))%MOD; return quick_Pow((x*x)%MOD,y/2); } void Prepare(){ F[0]=E[0]=1; for(int i=1;i<=MAXT;i++) F[i]=(F[i-1]*i)%MOD; E[MAXT]=quick_Pow(F[MAXT],MOD-2); for(int i=MAXT-1;i>=1;i--) E[i]=(E[i+1]*(i+1))%MOD; } void DFS(int u,int fa){ Siz[u]=1;H[u]=C;Dp[u]=1; int size=P[u].size(); for(int i=0;i<size;i++){ int v=P[u][i]; if(v==fa)continue; DFS(v,u);Son[u]++; Dp[u]=(Dp[u]*Dp[v])%MOD; Siz[u]+=Siz[v]; H[u]=(H[u]+Mul(H[v],T[Siz[v]]))%LW; if(!Cnt[u][H[v]])Kind[u].push_back(H[v]); Cnt[u][H[v]]++; } Dp[u]=(Dp[u]*F[Son[u]])%MOD; size=Kind[u].size(); for(int i=0;i<size;i++){ ULL v=Kind[u][i]; Dp[u]=(Dp[u]*E[Cnt[u][v]])%MOD; } } void DFS2(int u,int fa){ int size=P[u].size(); for(int i=0;i<size;i++){ int v=P[u][i]; if(v==fa)continue; ULL Hu=(H[u]-Mul(T[Siz[v]],H[v])+LW)%LW; ULL Dpu=Dp[u]*E[Son[u]]%MOD*F[Son[u]-1]%MOD*F[Cnt[u][H[v]]]%MOD*E[Cnt[u][H[v]]-1]%MOD*quick_Pow(Dp[v],MOD-2)%MOD; H[v]=(H[v]+Mul(T[Siz[u]-Siz[v]],(H[u]-Mul(T[Siz[v]],H[v])+LW)))%LW; Dp[v]=Dp[v]*E[Son[v]]%MOD*F[Son[v]+1]%MOD*F[Cnt[v][Hu]]%MOD*E[Cnt[v][Hu]+1]%MOD*Dpu%MOD; Cnt[v][Hu]++;Son[v]++; Siz[v]=Siz[u]; DFS2(v,u); } } int main(){ srand(time(NULL)); scanf("%d",&K); for(int i=1;i<=MAXT;i++)T[i]=(rand()*rand()*rand())%LW+1; Prepare(); while(K--){ scanf("%d",&N); Mp.clear();Ans=0; for(int i=1;i<=N;i++){ H[i]=Siz[i]=0; Dp[i]=0;Son[i]=0; P[i].clear(); Kind[i].clear(); Cnt[i].clear(); } for(int i=1,x,y;i<N;i++){ scanf("%d%d",&x,&y); P[x].push_back(y); P[y].push_back(x); } DFS(1,0);DFS2(1,0); for(int i=1;i<=N;i++) if(!Mp[H[i]]){ Ans=(Ans+Dp[i])%MOD; Mp[H[i]]=1; } printf("%lld\n",Ans); } } ``` \]

posted @ 2019-11-07 09:58  孤攀客  阅读(268)  评论(0编辑  收藏  举报