题意:
给定一颗 n 个点的树,给定 m 条路径,计算有多少种不同的决策。
决策的定义:选 k 条路径,使得这 k 条路径至少有一个公共点。
解法:
对于计数问题,一般得找到一个基准点,来避免相同情况被重复计算,因为 k 条路径至少有一个公共点,所以此题的基准点可以着眼于树上的每个点上,找到对此题有用的基准点,然后对于该基准点进行代贡献计算,然后贡献相加即可。
因为此题有路径,不难想到基准点为路径端点的最近公共祖先。加以思考,每个点的贡献可为经过该点的路径中选取 k 条,并且这 k 条中有以该点为最近公共祖先的路径的情况数,这样这些路径对于该点来说就是特别的,情况不会被重复计算,并且也不会漏掉情况。
每个点被经过的次数可用树上差分记录。
贡献的计算可用组合数实现。
以下是 AC 代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 10, M = 6e5 + 10;
const int Mod = 1e9 + 7;
typedef long long LL;
int n, m, k;
int h[N], e[M], ne[M], idx;
int d[N], top[N];
LL ans;
void init()
{
memset(d, 0, sizeof d);
memset(top, 0, sizeof top);
memset(h, -1, sizeof h);
idx = 0;
ans = 0;
}
void add(int a, int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++;
}
LL mul[N];
void C_prework()
{
mul[0] = 1;
for (int i = 1; i <= 3e5; i ++)
mul[i] = (mul[i - 1] * i) % Mod;
}
LL quick_pow(LL a, int b)
{
LL ans = 1;
while (b)
{
if (b & 1) ans = (ans * a) % Mod;
a = (a * a) % Mod;
b >>= 1;
}
return ans;
}
LL inverse(LL a)
{
return quick_pow(a, Mod - 2);
}
LL C(int n, int m)
{
if (m > n) return 0;
return ((mul[n] * inverse(mul[n - m])) % Mod) * inverse(mul[m]) % Mod;
}
class LCA //树链剖分求LCA
{
public:
int f[N], top[N], dep[N];
int sz[N], son[N];
void dfs1(int u, int fa)
{
sz[u] = 1, dep[u] = dep[fa] + 1, f[u] = fa;
int cnt = 0;
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (j == fa) continue;
dfs1(j, u);
sz[u] += sz[j];
if (sz[j] > sz[cnt]) cnt = j;
}
son[u] = cnt;
}
void dfs2(int u, int t)
{
top[u] = t;
if (!son[u]) return;
dfs2(son[u], t);
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (j == f[u] || j == son[u]) continue;
dfs2(j, j);
}
}
void prework()
{
dfs1(1, 0);
dfs2(1, 1);
}
int lca(int a, int b)
{
while (top[a] != top[b])
{
if (dep[top[a]] < dep[top[b]]) swap(a, b);
a = f[top[a]];
}
return dep[a] < dep[b] ? a : b;
}
} G;
void dfs(int u, int fa)
{
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (j == fa) continue;
dfs(j, u);
d[u] += d[j];
}
ans = (ans + C(d[u], k) - C(d[u] - top[u], k) + Mod) % Mod;
}
int main()
{
//freopen("C:\\Users\\Super Yuxiao Guo\\Desktop\\Code_Place\\Data_Test\\data.in.txt", "r", stdin);
//freopen("C:\\Users\\Super Yuxiao Guo\\Desktop\\Code_Place\\Data_Test\\data.out.txt", "w", stdout);
std::ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
C_prework();
int T;
scanf("%d", &T);
while (T --)
{
init();
scanf("%d%d%d", &n, &m, &k);
for (int i = 1; i < n; i ++)
{
int a, b;
scanf("%d%d", &a, &b);
add(a, b), add(b, a);
}
G.prework();
for (int i = 0; i < m; i ++)
{
int a, b;
scanf("%d%d", &a, &b);
int lca = G.lca(a, b);
int f_lca = G.f[lca];
d[a] ++, d[b] ++, d[lca] --, d[f_lca] --;
top[lca] ++;
}
dfs(1, 0);
printf("%lld\n", ans);
}
return 0;
}