给定一棵 n n n 个点的树和 m m m 个树上的简单路径的端点点对,求从 m m m 条路里选 k k k 条,满足这 k k k 条路有至少一个点是公共点的方案数。
模拟赛的时候想着想着就以为是 k k k 条路连通了,于是写了个单点修改、区间查询的树链剖分,然后对路径排序,按照 l c a ( u , v ) lca(u, v) lca(u,v) 的深度从深往浅处理,每次先统计这条路径上的和 c n t cnt cnt(区间查询,贡献为 C ( c n t , k − 1 ) C(cnt, k - 1) C(cnt,k−1)),然后对 l c a ( u , v ) lca(u, v) lca(u,v) 的点加1(单点修改),答案非常正确,思路也是没错的,就是可惜看错了题意(呜呜)比赛的时候还以为我树链码炸了qwq
最后发现题解竟然是把我们赛时的思路完全倒过来,区间修改、单点查询,对路径的排序按照 l c a ( u , v ) lca(u, v) lca(u,v) 的深度从浅到深处理,每次计算 l c a ( u , v ) lca(u, v) lca(u,v) 的点的和 c n t cnt cnt(单点查询,贡献为 C ( c n t , k − 1 ) C(cnt, k - 1) C(cnt,k−1)),然后对路径上的所有点加1(区间修改)
改完一发过,很淦,究极板子叠加题
#include
using namespace std;
typedef long long ll;
const int N = 4e5 + 10;
const double pi = acos(-1.0);
const double eps = 1e-7;
const ll mod = 1e9 + 7;
struct line //线段树
{
struct node
{
int l, r;
int add;
ll sum;
} tr[N * 4];
void pushup(int u)
{
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
void pushdown(int u)
{
if(!tr[u].add)
return ;
auto &root = tr[u], &lson = tr[u << 1], &rson = tr[u << 1 | 1];
lson.add += root.add;
rson.add += root.add;
lson.sum += (lson.r - lson.l + 1) * root.add;
rson.sum += (rson.r - rson.l + 1) * root.add;
root.add = 0;
}
void build(int u, int l, int r)
{
tr[u] = {l, r, 0, 0};
if(l == r)
return ;
int mid = (l + r) >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
}
ll query(int u, int pos)
{
if(tr[u].l == pos && tr[u].r == pos)
{
return tr[u].sum;
}
else
{
pushdown(u);
ll res = 0;
int mid = (tr[u].l + tr[u].r) >> 1;
if(pos <= mid)
res = query(u << 1, pos);
if(pos > mid)
res += query(u << 1 | 1, pos);
return res;
}
}
void modify(int u, int l, int r, int val) ///tr[u] += val
{
if(tr[u].l >= l && tr[u].r <= r)
{
tr[u].sum += (tr[u].r - tr[u].l + 1) * val;
tr[u].add += val;
}
else
{
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
if(l <= mid)
modify(u << 1, l, r, val);
if(r > mid)
modify(u << 1 | 1, l, r, val);
pushup(u);
}
}
} line;
//下面是树链剖分
int head[N * 2], nxt[N * 2], es[N * 2],
dfsn[N], wson[N], top[N], sz[N], par[N], dep[N], idx, tot;
void addEdge(int u, int v)
{
es[++idx] = v;
nxt[idx] = head[u];
head[u] = idx;
}
void dfs1(int u, int fa)
{
par[u] = fa;
dep[u] = dep[fa] + 1;
sz[u] = 1;
for(int i = head[u]; ~i; i = nxt[i])
{
int v = es[i];
if(v == fa)
continue;
dfs1(v, u);
sz[u] += sz[v];
if(wson[u] == 0 || sz[wson[u]] < sz[v])
wson[u] = v;
}
}
void dfs2(int u, int tp)
{
dfsn[u] = ++tot;
top[u] = tp;
if(wson[u])
{
dfs2(wson[u], tp);
}
for(int i = head[u]; ~i; i = nxt[i])
{
int v = es[i];
if(v == par[u] || v == wson[u])
continue;
dfs2(v, v);
}
}
int lca(int u, int v)
{
while(top[u] != top[v])
{
if(dep[top[u]] >= dep[top[v]])
u = par[top[u]];
else
v = par[top[v]];
}
return dep[u] > dep[v] ? v : u;
}
ll query(int a, int b)
{
int res = lca(a, b);
return line.query(1, dfsn[res]);
}
void update(int a, int b)
{
ll res = 0;
while(top[a] != top[b])
{
if(dep[top[a]] < dep[top[b]])
swap(a, b);
line.modify(1, dfsn[top[a]], dfsn[a], 1);
a = par[top[a]];
}
if(dep[a] > dep[b])
swap(a, b);
line.modify(1, dfsn[a], dfsn[b], 1);
}
// 路径的结构体
struct path
{
int u, v, d;
bool operator < (const path& obj)
{
return d < obj.d;
}
};
path vec[N];
// 组合数
ll inv[N], fac[N], ifac[N];
void init_C()
{
fac[0] = ifac[0] = inv[0] = inv[1] = 1;
for(int i = 2; i <= N - 1; i++)
{
inv[i] = 1LL * (mod - mod / i) * inv[mod % i] % mod;
}
for(int i = 1; i <= N - 1; i++)
{
fac[i] = 1LL * fac[i - 1] * i % mod;
ifac[i] = 1LL * ifac[i - 1] * inv[i] % mod;
}
}
void init()
{
tot = 0;
idx = 0;
memset(head, -1, sizeof head);
memset(wson, 0, sizeof wson);
}
ll C(ll n, ll m)
{
if(n < m)
return 0;
if(m == 0 || n == m)
return 1;
return 1LL * fac[n] * ifac[m] % mod * ifac[n - m] % mod;
}
int main()
{
init_C();
int t;
scanf("%d", &t);
while (t--)
{
init();
int n, m, k;
scanf("%d%d%d", &n, &m, &k);
for(int i = 0, u, v; i < n - 1; i++)
{
scanf("%d%d", &u, &v);
addEdge(u, v);
addEdge(v, u);
}
dfs1(1, 0);
dfs2(1, 1);
line.build(1, 1, n);
for(int i = 0, u, v; i < m; i++)
{
scanf("%d%d", &u, &v);
vec[i] = {u, v, dep[lca(u, v)]};
}
sort(vec, vec + m);
ll ans = 0;
for(int i = 0; i < m; i++)
{
ll res = query(vec[i].u, vec[i].v);
ans = (ans + C(res, k - 1)) % mod;
update(vec[i].u, vec[i].v);
}
printf("%lld\n", ans);
}
return 0;
}
/*
*/