樹的拓撲序計數

congmingyige發表於2024-09-12

樹的拓撲序計數:樹走拓撲排序,從根節點出發,每次只能從已遍歷的點延伸到下一個相鄰點,把樹的節點都遍歷完,所有遍歷方式的情況數目?

對於一棵子樹,它裡面有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 卡特蘭數

相關文章