链接
题解来源:点击打开链接
题意:
给定n k
下面n-1行给出一棵树。
把数字1-n填到树的节点上。
填完后计算leader节点个数,若这个点是leader,则这个点上填的数>这个点的子树上填的数
问:恰好有k个leader节点的 填涂方案数.
思路:
dp[i][j]表示以i点为根的子树 有恰好j个leader的方案数。
如果u 是叶子节点则 dp[u][0] = 0, dp[u][1] = 1;
如果u不是叶子节点:
先不考虑u点能否成为leader,背包一下。
然后考虑u点:若u能成为leader,设siz[u]表示u的子树节点个数。
那么对于u的子树来说,要把[1, siz[u] ]填到子树上,当u是leader, u只能填 siz[u]
而子树的分配方案就是一个多重集的排列,因为分配给子树的是组合,子树之间是排列。
设u成为leader的方法数为 x1
x1 = (siz[u]-1)! / siz[v1]! / siz[v2]! ····
那么dp[u][i] = dp[u][i] * (u的子树填涂的总方案数 - x1) + dp[u][i-1] * x1
#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <cstdio> #include <algorithm> #include <string> #include <cmath> #include <cstring> #include <queue> #include <set> #include <map> #include <vector> using namespace std; template <class T> inline bool rd(T &ret) { char c; int sgn; if (c = getchar(), c == EOF) return 0; while (c != '-' && (c<'0' || c>'9')) c = getchar(); sgn = (c == '-') ? -1 : 1; ret = (c == '-') ? 0 : (c - '0'); while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0'); ret *= sgn; return 1; } template <class T> inline void pt(T x) { if (x < 0) { putchar('-'); x = -x; } if (x > 9) pt(x / 10); putchar(x % 10 + '0'); } typedef pair<int, int> pii; typedef long long ll; const int N = 1005; const int mod = 1e9 + 7; const int inf = 1e9; int Pow(int x, int y) { int ans = 1; while (y) { if (y & 1)ans = (ll)ans*x%mod; y >>= 1; x = (ll)x*x%mod; }return ans; } vector<int>G[N]; int n, k; int dp[N][N], siz[N], lef[N]; int A[N]; int mul(int x, int y) { x = (ll)x*y%mod; return x; } inline void add(int &x, int y) { x += y; if (x >= mod)x -= mod; } inline void sub(int &x, int y) { x -= y; if (x < 0)x += mod; } inline void dv(int &x, int y) { x = (ll)x*Pow(y, mod - 2) % mod; } int g[N]; void dfs(int u, int fa) { siz[u] = 1; lef[u] = 0; for (auto v : G[u]) { if (v == fa)continue; dfs(v, u); siz[u] += siz[v]; } if (siz[u] == 1) { dp[u][0] = 0; dp[u][1] = 1; lef[u] = 1; return; } dp[u][0] = 1; int x1 = A[siz[u] - 1], x2 = A[siz[u]]; siz[u] = 0; for (auto v : G[u]) { if (v == fa)continue; for (int i = lef[u] + lef[v]; i <= min(k, siz[u] + siz[v]); i++)g[i] = 0; for (int i = lef[u]; i <= min(k, siz[u]); i++) { for (int j = lef[v]; j <= min(k, siz[v]) && i + j <= k; j++) { add(g[i + j], mul(dp[v][j], dp[u][i])); } } for (int i = lef[u] + lef[v]; i <= min(k, siz[u] + siz[v]); i++)dp[u][i] = g[i]; siz[u] += siz[v]; lef[u] += lef[v]; dv(x1, A[siz[v]]); dv(x2, A[siz[v]]); } siz[u]++; sub(x2, x1); for (int i = min(siz[u], k); i >= lef[u]; i--) { int tmp = 0; add(tmp, mul(dp[u][i], x2)); if (i - 1 >= lef[u]) add(tmp, mul(dp[u][i - 1], x1)); dp[u][i] = tmp; } } int main() { A[0] = 1; for (int i = 1; i < N; i++)A[i] = (ll)A[i - 1] * i%mod; int T, Cas = 1; rd(T); while (T--) { rd(n); rd(k); for (int i = 1; i <= n; i++)G[i].clear(), memset(dp[i], 0, sizeof dp[i]); for (int i = 1, u, v; i < n; i++) { rd(u); rd(v); G[u].push_back(v); G[v].push_back(u); } dfs(1, 1); printf("Case #%d: ", Cas++); pt(dp[1][k]); puts(""); } return 0; } /* 99 5 3 1 2 2 3 2 4 1 5 4 2 1 2 2 3 2 4 ans:12 4 3 1 2 2 3 2 4 4 4 1 2 2 3 2 4 */
2 3 2 1 2 1 3 10 8 2 1 3 2 4 1 5 3 6 1 7 3 8 7 9 7 10 6
Case #1: 4 Case #2: 316512