樹的拓撲序計數:樹走拓撲排序,從根節點出發,每次只能從已遍歷的點延伸到下一個相鄰點,把樹的節點都遍歷完,所有遍歷方式的情況數目?
對於一棵子樹,它裡面有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 }
C-序列_牛客挑戰賽76 (nowcoder.com)
[ () () () ]
[] 根節點 () 葉子節點
如果相鄰滿足左右括號 ( ) 情況則被合併。括號不斷、依次被合併,括號的串逐漸變小。括號的合併方式具有唯一性。
要確保葉子節點先走(被合併),根節點在這些葉子節點走完了(被合併完了),才能走(才被合併)。
樹的拓撲序計數
類似但實際不同:
括號序列 - OI Wiki (oi-wiki.org)
構造方式數目: catalan 卡特蘭數