# tioj 2043 魔法鏈 https://tioj.ck.tp.edu.tw/problems/2043 * 樹dp * 如果跟節點度數>1,那一定是從葉節點往上拔。事實上根節點度數=1也一樣從下面拔就好,因為我們會枚舉以每個點為根所形成的樹,如此就會發現不用擔心少算從根節點拔的情況了(因為那相當於以其它為根由下往上拔)。 * 如此樹dp的狀態就都一樣了,dp[v]代表把v的子樹都拔完的方法樹(v最後拔)。 * v下面的子樹不能亂拔一通,ex:如果要拔掉某一個child,就要先把那個child下的子樹都拔完才能拔自己。但如果是不同子樹就可以交叉亂拔 * 這時候會發現這其實很像高一下排列組合經典題型,"甲~戊中,丙一定要在甲、乙後面的排列數"。而這個的做法就是把甲乙丙都當作同一個東西做排列,然後再把甲乙隨機排列(2!)。 * 轉移:就照著上述的排列組合題型解法 * 注意要枚舉每個根 ```cpp= #include <bits/stdc++.h> #define pb push_back #define f first #define s second #define rep(X, a,b) for(int X=a;X<b;++X) #define ALL(a) (a).begin(), (a).end() #define SZ(a) (int)(a).size() #define NL "\n" #pragma GCC optimize("Ofast") using namespace std; typedef pair<long long,long long> pll; typedef pair<int,int> pii; typedef long long ll; vector<int> adj[2010]; ll res[2010], sz[2010], fact[10000]; const ll mod=1e9+7; ll fpow(ll a, ll b) { ll res = 1; while (b > 0) { if (b & 1) res = res * a%mod; a = a * a%mod; b >>= 1; } return res; } void dfs(int v, int p){ res[v]=sz[v]=1; int tot=0; for(auto a:adj[v]){ if(a!=p){ dfs(a, v); sz[v]+=sz[a]; tot+=sz[a]; } } if(sz[v]==1) return; res[v]=fact[tot]; for(auto a:adj[v]) if(a!=p) res[v]=res[v]*fpow(fact[sz[a]], mod-2)%mod*res[a]%mod; } int main(){ ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0); fact[1]=1; rep(i,2,10001) fact[i]=fact[i-1]*i%mod; int n, a, b; cin>>n; rep(i,0,n-1){ cin>>a>>b; adj[a].pb(b); adj[b].pb(a); } ll ans=0; rep(i,1,n+1){ dfs(i, 0); ans+=res[i]; ans%=mod; } cout<<ans<<NL; } ```