原题
题目描述
给定一棵有 $n$ 个结点的树 $T$,结点依次以 $1,2,\dots,n$ 标号。树 $T$ 的深度优先遍历序可由以下过程得到:
- 选定深度优先遍历的起点 $s$($1 \leq s \leq n$),当前位置结点即是起点。
- 若当前结点存在未被遍历的相邻结点 $u$ 则遍历 $u$,也即令当前位置结点为 $u$ 并重复这一步;否则回溯。
- 按照遍历结点的顺序依次写下结点编号,即可得到一组深度优先遍历序。
第一步中起点的选择是任意的,并且第二步中遍历相邻结点的顺序也是任意的,因此对于同一棵树 $T$ 可能有多组不同的深度优先遍历序。请你求出树 $T$ 有多少组不同的深度优先遍历序。由于答案可能很大,你只需要求出答案对 $10^9$ 取模之后的结果。
输入格式
第一行,一个整数 $n$,表示树 $T$ 的结点数。
接下来 $n-1$ 行,每行两个正整数 $u_i, v_i$,表示树 $T$ 中的一条连接结点 $u_i, v_i$ 的边。
输出格式
输出一行,一个整数,表示树 $T$ 的不同的深度优先遍历序数量对 $10^9$ 取模的结果。
输入输出样例 #1
输入 #1
输出 #1
输入输出样例 #2
输入 #2
1 2 3 4 5 6 7 8
| 8 1 2 1 3 1 4 2 5 2 6 3 7 3 8
|
输出 #2
说明/提示
对于 40% 的测试点,保证 $1 \leq n \leq 8$。
对于另外 20% 的测试点,保证给定的树是一条链。
对于所有测试点,保证 $1 \leq n \leq 10^5$。
解
题目表面上说是多少种深搜的序列,实际上深搜不是重点,重点是求排列数。
40分代码
对于每一个节点,假设它有 $n$ 个子节点,那么可以选择的,抛开子节点的子节点不看,遍历方法就有 $A_n^n$ 即 $n!$ 种。
因此,总的方案数就是所有的节点的子节点数阶乘的总乘积。
时间复杂度 $O(n^2)$ 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| #include<bits/stdc++.h> using namespace std; int n,u,v,ans,sum; const int N=1e5+5; vector<int>g[N]; int f[N]; const int mod=1e9; bool vis[N]; void dfs(int x,int fa){ ans=(ans*f[(fa==-1?g[x].size():g[x].size()-1)])%mod; for(auto i:g[x]){ if(!vis[i]){ vis[i]=1; dfs(i,x); } } } int main(){ cin>>n; f[0]=1; for(int i=1;i<=n;i++){ f[i]=(f[i-1]*i)%mod; } for(int i=1;i<n;i++){ cin>>u>>v; g[u].push_back(v); g[v].push_back(u); } for(int i=1;i<=n;i++){ memset(vis,0,sizeof(vis)); vis[i]=1; ans=1; dfs(i,-1); sum=(sum+ans)%mod; } cout<<sum; return 0; }
|
60分代码
观察每次的乘积可知,每次所有的子节点都乘以相同的因数(连边数-1),所以可以一次性算出来,由于起始节点要乘 $n!$ 而不是 $(n-1)!$ ,所以再乘 $n$ 即可。
时间复杂度 $O(n)$ 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #include<bits/stdc++.h> using namespace std; #define int long long int n,u,v,mul=1,sum; const int N=1e5+5; vector<int>g[N]; int f[N]; const int mod=1e9; bool vis[N]; void dfs(int x){ mul=(mul*f[g[x].size()-1])%mod; for(auto i:g[x]){ if(!vis[i]){ vis[i]=1; dfs(i); } } } signed main(){ cin>>n; f[0]=1; for(int i=1;i<=n;i++){ f[i]=(f[i-1]*i)%mod; } for(int i=1;i<n;i++){ cin>>u>>v; g[u].push_back(v); g[v].push_back(u); } vis[1]=1; dfs(1); for(int i=1;i<=n;i++){ int res=(mul*g[i].size())%mod; sum=(sum+res)%mod; } cout<<sum; return 0; }
|
AC代码
考虑特殊情况,当 $n=1$ 时,由于这个节点没有子节点所以连边数减 $1$ 会得到 $0$ ,造成 mul
得到 $0$。
所以再加一个特判得全分。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| #include<bits/stdc++.h> using namespace std; #define int long long int n,u,v,mul=1,sum; const int N=1e5+5; vector<int>g[N]; int f[N]; const int mod=1e9; bool vis[N]; void dfs(int x){ mul=(mul*f[g[x].size()-1])%mod; for(auto i:g[x]){ if(!vis[i]){ vis[i]=1; dfs(i); } } } signed main(){ cin>>n; f[0]=1; for(int i=1;i<=n;i++){ f[i]=(f[i-1]*i)%mod; } for(int i=1;i<n;i++){ cin>>u>>v; g[u].push_back(v); g[v].push_back(u); } vis[1]=1; dfs(1); for(int i=1;i<=n;i++){ int res=(mul*g[i].size())%mod; sum=(sum+res)%mod; } if(n==1)cout<<1; else{ cout<<sum; } return 0; }
|
Admibrill
Run the code, run after the code