很顯然能夠用一個揹包算出來湊齊i個位置的方案c++
而後總的答案就是\(dp_{n / 2}\)spa
而後須要扣掉不符合條件的就是把選出來的數的貢獻剪掉的貢獻code
而後注意由於是多重集合的排列,因此須要乘上\(\frac{fac[n / 2]}{fac[cnt_a]fac[cnt_b].....}\ast \frac{fac[n / 2]}{fac[cnt_c]fac[cnt_d].....}\)input
而後顯然下面是全部個數的階乘積,而後沒了it
#include<bits/stdc++.h> using namespace std; const int N = 1e5 + 10; const int M = 60; const int Mod = 1e9 + 7; int add(int a, int b) { return (a += b) >= Mod ? a - Mod : a; } int sub(int a, int b) { return (a -= b) < 0 ? a + Mod : a; } int mul(int a, int b) { return 1ll * a * b % Mod; } int fast_pow(int a, int b) { int res = 1; for (; b; b >>= 1, a = mul(a, a)) if (b & 1) res = mul(res, a); return res; } int inv[N], fac[N]; int ans[M][M] = {0}, cnt[M]; int n, q, f[N]; char s[N]; int id(char c) { if ('a' <= c && c <= 'z') { return c - 'a' + 27; } else { return c - 'A' + 1; } } int C(int a, int b) { return a >= b ? mul(fac[a], mul(inv[b], inv[a - b])) : 0; } void modify(int id, int typ) { if (typ) { for (int i = n / 2; i >= cnt[id]; i--) f[i] = add(f[i], f[i - cnt[id]]); } else { for (int i = cnt[id]; i <= n / 2; i++) f[i] = sub(f[i], f[i - cnt[id]]); } } int main() { #ifdef dream_maker freopen("input.txt", "r", stdin); #endif scanf("%s", s + 1); n = strlen(s + 1); inv[0] = fac[0] = 1; for (int i = 1; i <= n; i++) fac[i] = mul(fac[i - 1], i); inv[n] = fast_pow(fac[n], Mod - 2); for (int i = n - 1; i >= 1; i--) inv[i] = mul(inv[i + 1], i + 1); for (int i = 1; i <= n; i++) cnt[id(s[i])]++; f[0] = 1; for (int i = 1; i <= 52; i++) if (cnt[i]) modify(i, 1); for (int i = 1; i <= 52; i++) if (cnt[i] && cnt[i] <= n / 2) { modify(i, 0); ans[i][i] = f[n / 2 - cnt[i]]; modify(i, 1); } for (int i = 1; i <= 52; i++) if (cnt[i]) { modify(i, 0); for (int j = 1; j <= 52; j++) if (i != j && cnt[j] && cnt[j] + cnt[i] <= n / 2) { modify(j, 0); ans[i][j] = f[n / 2 - cnt[i] - cnt[j]]; modify(j, 1); } modify(i, 1); } int bas = mul(fac[n / 2], fac[n / 2]); for (int i = 1; i <= 52; i++) bas = mul(bas, inv[cnt[i]]); for (int i = 1; i <= 52; i++) for (int j = 1; j <= 52; j++) ans[i][j] = mul(ans[i][j], mul(2, bas)); scanf("%d", &q); while (q--) { int x, y; scanf("%d %d", &x, &y); printf("%d\n", ans[id(s[x])][id(s[y])]); } return 0; }
首先若是在虛樹上考慮,咱們按照深度進行dpast
發現\(f_{i,j}\)表示i個點分紅j個集合的方案數有轉移:class
\(f_{i,j}=f_{i - 1,j}\ast (j - h_i)+f_{i - 1,j - 1}\)sort
其中h是一個節點的父親個數static
而後h咋算呢?集合
就是能夠用i到1的節點個數加上r到1的節點個數減去lca到1的節點個數的兩倍
而後以1爲根的時候每個點在dfs序上的貢獻都是一個區間,用bit算一下就能夠了
而後就直接dp就能夠了
由於有屢次詢問,因此注意邊界就能夠了
#include<bits/stdc++.h> using namespace std; const int N = 1e5 + 10; const int M = 5e2 + 10; const int LOG = 20; const int Mod = 1e9 + 7; int add(int a, int b) { return (a += b) >= Mod ? a - Mod : a; } int mul(int a, int b) { return 1ll * a * b % Mod; } int n, q, f[N][M]; vector<int> g[N]; int dep[N], fa[N][LOG]; int bg[N], ed[N], ind = 0; void dfs(int u, int father) { dep[u] = dep[father] + 1; bg[u] = ++ind; fa[u][0] = father; for (int i = 1; i < 18; i++) fa[u][i] = fa[fa[u][i - 1]][i - 1]; for (auto v : g[u]) if (v != father) dfs(v, u); ed[u] = ind; } int lca(int x, int y) { if (dep[x] < dep[y]) swap(x, y); int delta = dep[x] - dep[y]; for (int i = 0; i < 18; i++) if ((delta >> i) & 1) x = fa[x][i]; if (x == y) return x; for (int k = 17; k >= 0; k--) { if (fa[x][k] != fa[y][k]) { x = fa[x][k]; y = fa[y][k]; } } return fa[x][0]; } int bit[N]; void modify(int t, int vl) { for (; t <= n; t += t & (-t)) bit[t] += vl; } int query(int t) { int res = 0; for (; t; t -= t & (-t)) res += bit[t]; return res; } void solve() { static int h[N], p[N], k, m, r; static bool mark[N]; scanf("%d %d %d", &k, &m, &r); for (int i = 1; i <= k; i++) scanf("%d", &p[i]); for (int i = 1; i <= k; i++) { modify(bg[p[i]], 1); modify(ed[p[i]] + 1, -1); mark[p[i]] = 1; } int hrt = query(bg[r]); for (int i = 1; i <= k; i++) { int g = lca(p[i], r); h[i] = query(bg[p[i]]) + hrt - 2 * query(bg[g]) + mark[g] - 1; } for (int i = 1; i <= k; i++) { modify(bg[p[i]], -1); modify(ed[p[i]] + 1, 1); mark[p[i]] = 0; } sort(h + 1, h + k + 1); // 至關於在虛樹上按照深度進行dp f[0][0] = 1; for (int i = 1; i <= k; i++) { for (int j = 1; j < h[i]; j++) f[i][j] = 0; for (int j = h[i]; j <= min(i, m); j++) f[i][j] = add(mul(j - h[i], f[i - 1][j]), f[i - 1][j - 1]); } int res = 0; for (int i = 1; i <= m; i++) res = add(res, f[k][i]); printf("%d\n", res); } int main() { #ifdef dream_maker freopen("input.txt", "r", stdin); #endif scanf("%d %d", &n, &q); for (int i = 1; i < n; i++) { int u, v; scanf("%d %d", &u, &v); g[u].push_back(v); g[v].push_back(u); } dfs(1, 0); while (q--) solve(); return 0; }