# Description
Luogu 传送门
# Solution
首先我们计算出整棵树的 序,然后一个点的祖先的 序一定小于当前点的 序。
我们把输入的关键点按照 序从小到大排序,对于一个关键点 ,所有不能和它放到同一组的点一定在它前面。
考虑依次加入每个关键点,假设 为 到根路径上的关键点个数, 表示把前 个关键点分成 组的方案数,那么有:
前者是把 放到其他组里的方案数,后者是 自成一组的方案(其实类似于第二类斯特林数)。
那如何求 呢?
每处理一个关键点打个标记,然后数 到根的标记个数。
先树剖一下,然后树状数组区间加区间查询即可。
#include <bits/stdc++.h> | |
#define pb push_back | |
using namespace std; | |
namespace IO{ | |
inline int read(){ | |
int x = 0; | |
char ch = getchar(); | |
while(!isdigit(ch)) ch = getchar(); | |
while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar(); | |
return x; | |
} | |
template <typename T> inline void write(T x){ | |
if(x > 9) write(x / 10); | |
putchar(x % 10 + '0'); | |
} | |
} | |
using namespace IO; | |
const int N = 1e5 + 200; | |
const int mod = 1e9 + 7; | |
int n, q, k, m, r; | |
int p[N], f[N], dp[N][310]; | |
vector <int> g[N]; | |
inline bool cmp(int x, int y){ | |
return f[x] < f[y]; | |
} | |
int fa[N], dep[N], siz[N], son[N]; | |
inline void dfs1(int x, int f){ | |
fa[x] = f, dep[x] = dep[f] + 1, siz[x] = 1; | |
for(auto y : g[x]){ | |
if(y == f) continue; | |
dfs1(y, x); | |
siz[x] += siz[y]; | |
if(siz[y] > siz[son[x]]) son[x] = y; | |
} | |
} | |
int top[N], dfn[N], tim; | |
inline void dfs2(int x, int topfa){ | |
top[x] = topfa, dfn[x] = ++tim; | |
if(son[x]) dfs2(son[x], topfa); | |
for(auto y : g[x]) | |
if(y != fa[x] && y != son[x]) dfs2(y, y); | |
} | |
int c[N]; | |
inline void add(int x, int y){ | |
for(; x <= n; x += x & (-x)) c[x] += y; | |
} | |
inline int qry(int l, int r){ | |
int res = 0; | |
for(; r; r -= r & (-r)) res += c[r]; | |
for(--l; l; l -= l & (-l)) res -= c[l]; | |
return res; | |
} | |
inline int query(int x, int y){ | |
int res = 0; | |
while(top[x] != top[y]){ | |
if(dep[top[x]] < dep[top[y]]) swap(x, y); | |
res += qry(dfn[top[x]], dfn[x]); | |
x = fa[top[x]]; | |
} | |
if(dfn[x] > dfn[y]) swap(x, y); | |
res += qry(dfn[x], dfn[y]); | |
return res; | |
} | |
inline void Clear(){ | |
for(int i = 1; i <= k; ++i){ | |
for(int j = 1; j <= min(i, m); ++j) dp[i][j] = 0; | |
f[p[i]] = 0, add(dfn[p[i]], -1); | |
} | |
} | |
signed main(){ | |
n = read(), q = read(); | |
for(int i = 1; i < n; ++i){ | |
int u = read(), v = read(); | |
g[u].pb(v), g[v].pb(u); | |
} | |
dfs1(1, 0), dfs2(1, 1); | |
while(q--){ | |
k = read(), m = read(), r = read(); | |
for(int i = 1; i <= k; ++i) add(dfn[p[i] = read()], 1); | |
for(int i = 1; i <= k; ++i) f[p[i]] = query(p[i], r) - 1; | |
sort(p + 1, p + 1 + k, cmp); | |
dp[1][1] = 1; | |
for(int i = 2; i <= k; ++i) | |
for(int j = 1; j <= min(i, m); ++j) | |
dp[i][j] = (1ll * max(j - f[p[i]], 0) * dp[i - 1][j] + dp[i - 1][j - 1]) % mod; | |
int ans = 0; | |
for(int i = 1; i <= m; ++i) ans = (ans + dp[k][i]) % mod; | |
write(ans), puts(""), Clear(); | |
} | |
} |