树的拓扑序计数

 

树的拓扑序计数:树走拓扑排序,从根节点出发,每次只能从已遍历的点延伸到下一个相邻点,把树的节点都遍历完,所有遍历方式的情况数目?

 

对于一棵子树,它里面有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 卡特兰数 

 

posted @ 2024-09-12 01:29  congmingyige  阅读(92)  评论(0编辑  收藏  举报