树的拓扑序计数
树的拓扑序计数:树走拓扑排序,从根节点出发,每次只能从已遍历的点延伸到下一个相邻点,把树的节点都遍历完,所有遍历方式的情况数目?
对于一棵子树,它里面有k个点,可以有k!操作情况,但要确保根节点先走,剩下随意,可以有(k-1)!操作情况(根节点先走,就确定了一个位置,剩余k-1个位置),相当于/k。
对于当前树的所有节点和其子树,都是这样,/siz(tree_i)
结果为 n! / siz(tree_i)
F - Distributing Integers (atcoder.jp)
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define LL long long 4 #define ULL unsigned long long 5 6 const LL mod=1e9+7; 7 8 const double eps_1=1e-5; 9 const double eps_2=1e-10; 10 11 const int maxn=2e5+10; 12 13 vector<LL> adj[maxn]; 14 LL n,siz[maxn],cheng[maxn],ni[maxn],result[maxn]; //,ni_cheng[maxn] 15 bool vis[maxn]; 16 17 void dfs(LL d) 18 { 19 siz[d]++; 20 vis[d]=1; 21 for (LL child:adj[d]) 22 if (!vis[child]) 23 { 24 dfs(child); 25 siz[d]+=siz[child]; 26 } 27 (result[1] *= ni[siz[d]]) %= mod; 28 } 29 30 LL mul(LL a, LL b) 31 { 32 LL ans=1; 33 while (b) 34 { 35 if (b&1) 36 ans=ans*a%mod; 37 a=a*a%mod; 38 b>>=1; 39 } 40 return ans; 41 } 42 43 void modify(LL d) 44 { 45 vis[d]=1; 46 for (LL child:adj[d]) 47 if (!vis[child]) 48 { 49 result[child] = result[d] * siz[child] %mod * ni[n-siz[child]] %mod; 50 modify(child); 51 } 52 } 53 54 int main() 55 { 56 LL u,v,i; 57 cin>>n; 58 cheng[0]=1; 59 for (i=1;i<=n;i++) 60 cheng[i]=cheng[i-1]*i%mod; 61 /* 62 ni_cheng[n]=mul(cheng[n],mod-2); 63 for (i=n-1;i>=0;i--) 64 ni_cheng[i]=ni_cheng[i+1]*(i+1)%mod; 65 ni[0]=1; 66 for (i=1;i<=n;i++) 67 ni[i]=cheng[i-1]*ni_cheng[i]%mod; 68 */ 69 70 ni[0]=1; 71 for (i=1;i<=n;i++) 72 ni[i] = mul(i, mod-2); 73 74 for (i=1;i<n;i++) 75 { 76 cin>>u>>v; 77 adj[u].push_back(v); 78 adj[v].push_back(u); 79 } 80 81 memset(vis,0,sizeof(vis)); 82 memset(siz,0,sizeof(siz)); 83 result[1]=cheng[n]; 84 dfs(1); 85 86 memset(vis,0,sizeof(vis)); 87 modify(1); 88 89 for (i=1;i<=n;i++) 90 cout<<result[i]<<endl; 91 92 93 return 0; 94 }
这样写可以减少求逆元的操作数:
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define LL long long 4 #define ULL unsigned long long 5 6 const LL mod=1e9+7; 7 8 const double eps_1=1e-5; 9 const double eps_2=1e-10; 10 11 const int maxn=2e5+10; 12 13 vector<LL> adj[maxn]; 14 LL n,siz[maxn],cheng[maxn],ni[maxn],result[maxn]; //,ni_cheng[maxn] 15 LL ni_cheng[maxn]; 16 bool vis[maxn]; 17 18 void dfs(LL d) 19 { 20 siz[d]++; 21 vis[d]=1; 22 for (LL child:adj[d]) 23 if (!vis[child]) 24 { 25 dfs(child); 26 siz[d]+=siz[child]; 27 } 28 (result[1] *= ni[siz[d]]) %= mod; 29 } 30 31 LL mul(LL a, LL b) 32 { 33 LL ans=1; 34 while (b) 35 { 36 if (b&1) 37 ans=ans*a%mod; 38 a=a*a%mod; 39 b>>=1; 40 } 41 return ans; 42 } 43 44 void modify(LL d) 45 { 46 vis[d]=1; 47 for (LL child:adj[d]) 48 if (!vis[child]) 49 { 50 result[child] = result[d] * siz[child] %mod * ni[n-siz[child]] %mod; 51 modify(child); 52 } 53 } 54 55 int main() 56 { 57 LL u,v,i; 58 cin>>n; 59 cheng[0]=1; 60 for (i=1;i<=n;i++) 61 cheng[i]=cheng[i-1]*i%mod; 62 63 ni_cheng[n]=mul(cheng[n],mod-2); 64 for (i=n-1;i>=0;i--) 65 ni_cheng[i]=ni_cheng[i+1]*(i+1)%mod; 66 ni[0]=1; 67 for (i=1;i<=n;i++) 68 ni[i]=cheng[i-1]*ni_cheng[i]%mod; 69 70 71 /* 72 ni[0]=1; 73 for (i=1;i<=n;i++) 74 ni[i] = mul(i, mod-2); 75 */ 76 77 for (i=1;i<n;i++) 78 { 79 cin>>u>>v; 80 adj[u].push_back(v); 81 adj[v].push_back(u); 82 } 83 84 memset(vis,0,sizeof(vis)); 85 memset(siz,0,sizeof(siz)); 86 result[1]=cheng[n]; 87 dfs(1); 88 89 memset(vis,0,sizeof(vis)); 90 modify(1); 91 92 for (i=1;i<=n;i++) 93 cout<<result[i]<<endl; 94 95 96 return 0; 97 }
[ () () () ]
[] 根节点 () 叶子节点
如果相邻满足左右括号 ( ) 情况则被合并。括号不断、依次被合并,括号的串逐渐变小。括号的合并方式具有唯一性。
要确保叶子节点先走(被合并),根节点在这些叶子节点走完了(被合并完了),才能走(才被合并)。
树的拓扑序计数
类似但实际不同:
构造方式数目: catalan 卡特兰数