CF1111E Tree
Posted venividivici
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF1111E Tree相关的知识,希望对你有一定的参考价值。
题意
给定一大小为 (N) 的树,共有 (Q) 次询问
第 (i) 次询问包含了三个数 (k_i,m_i,r_i),接着给定了树上互不相同的 (k_i) 个关键点 (a_{i,1},a_{i,2},a_{i,3}...a_{i,k})。对于第 (i) 次询问,你需要回答当这颗树以 (r_i) 为根时,有多少种方案将这 (k_i) 个点分为至多 (m_i) 组,使得同一组内的任意两个不同弄的结点都不存在祖先关系
对于第 (k) 次询问,假设你一共将 (k_i) 个点分为了 (p) 组,那么分组的方案需要满足:
- 给出的 (k_i) 个点中每个点属于且仅属于 (p) 组中的任意一组
- (p) 组中的任意一组非空
(N leq 10^5,Qleq 10^5,sum k_ileq 10^5)
解法
考虑 DP
把所有关键点按照一定顺序排好,使得点 (i) 的状态被更新当且仅当 (i) 的祖先的状态都已经被更新
设 (f[i][j]) 为把前 (i) 个点分为 (j) 组的方案数,那么有以下转移:
[
f[i][j]=f[i-1][j-1]+f[i-1][j] imes max(0, j-g_i)
]
其中 (g_i) 指的是第 (i) 个点到根节点的路径上的关键点个数(即祖先关键点的个数)
这个转移实际上是第二类斯特林数的转移,只不过带了一些限制:当前的点不能与其祖先所在的集合合并(显然其祖先互相之间一定也不属于同一集合)
(g_i) 可以直接用树剖加树状数组维护,现在的问题就是如何安排 DP 转移的顺序了
发现按照 (g_i) 从小到大的顺序转移也是完全没有问题的:因为祖先的 (g) 一定比儿子的小(不能用 DFS 序的原因是复杂度无法保证)
代码
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 1e5 + 10;
const int mod = 1e9 + 7;
int read();
void dfs_1(int x);
void dfs_2(int x, int tp);
int N, Q;
int f[MAX_N][310];
int fa[MAX_N], sz[MAX_N], top[MAX_N], dep[MAX_N], son[MAX_N], dfn[MAX_N], cnt;
int head[MAX_N], to[MAX_N << 1], nxt[MAX_N << 1], cap;
inline void swap(int& x, int& y) { x ^= y ^= x ^= y; }
inline void inc(int& x, int y) { (x += y) >= mod ? x -= mod : 0; }
inline int mul(int x, int y) { return 1LL * x * y % mod; }
inline int min(int x, int y) { return x < y ? x : y; }
inline int max(int x, int y) { return x > y ? x : y; }
struct BIT {
int c[MAX_N];
void ins(int x, int v) {
for (; x && x <= N; x += x & -x) c[x] += v;
}
int query(int x, int res = 0) {
for (; x; x -= x & -x) res += c[x];
return res;
}
} tr;
struct node {
int x, g;
bool operator < (const node& rhs) const { return g < rhs.g; }
} a[MAX_N];
inline void link(int x, int y) {
to[++cap] = y, nxt[cap] = head[x], head[x] = cap;
to[++cap] = x, nxt[cap] = head[y], head[y] = cap;
}
int query(int u, int v) {
int res = 0;
while (top[u] ^ top[v]) {
if (dep[top[u]] < dep[top[v]]) swap(u, v);
res += tr.query(dfn[u]) - tr.query(dfn[top[u]] - 1);
u = fa[top[u]];
}
if (dep[u] < dep[v]) swap(u, v);
res += tr.query(dfn[u]) - tr.query(dfn[v] - 1);
return res - 1;
}
int main() {
N = read(), Q = read();
for (int i = 1; i < N; ++i) link(read(), read());
dep[1] = 1;
dfs_1(1);
dfs_2(1, 1);
int k, m, r;
while (Q--) {
k = read(), m = read(), r = read();
for (int i = 1; i <= k; ++i) tr.ins(dfn[a[i].x = read()], 1);
for (int i = 1; i <= k; ++i) a[i].g = query(a[i].x, r);
sort(a + 1, a + k + 1);
f[0][0] = 1;
for (int i = 1; i <= k; ++i)
for (int j = 1; j <= m; ++j)
inc(f[i][j], (f[i - 1][j - 1] + mul(f[i - 1][j], max(0, j - a[i].g))) % mod);
int res = 0;
for (int i = 1; i <= m; ++i) inc(res, f[k][i]);
for (int i = 1; i <= k; ++i) tr.ins(dfn[a[i].x], -1);
for (int i = 1; i <= k; ++i)
for (int j = 1; j <= m; ++j) f[i][j] = 0;
printf("%d
", res);
}
return 0;
}
void dfs_1(int x) {
sz[x] = 1;
for (int i = head[x]; i; i = nxt[i])
if (to[i] != fa[x]) {
fa[to[i]] = x, dep[to[i]] = dep[x] + 1, dfs_1(to[i]), sz[x] += sz[to[i]];
if (sz[to[i]] > sz[son[x]]) son[x] = to[i];
}
}
void dfs_2(int x, int tp) {
top[x] = tp, dfn[x] = ++cnt;
if (son[x]) {
dfs_2(son[x], tp);
for (int i = head[x]; i; i = nxt[i])
if (!dfn[to[i]]) dfs_2(to[i], to[i]);
}
}
int read() {
int x = 0, c = getchar();
while (!isdigit(c)) c = getchar();
while (isdigit(c)) x = x * 10 + c - 48, c = getchar();
return x;
}
以上是关于CF1111E Tree的主要内容,如果未能解决你的问题,请参考以下文章