G. Rikka with Intersections of Paths
time limit per test
6.0 s
memory limit per test
1024 MB
input
standard input
output
standard output
Rikka has a tree TT with nn vertices numbered from 11 to nn .
Meanwhile, Rikka has marked mm simple paths in TT , the ii -th of which is between the vertices xixi and yiyi , where some of them could be the same path.
Now, Rikka wants to know in how many different strategies she can select kk paths from the marked paths such that those selected paths share at least one common vertex.
Input
The input contains several test cases, and the first line contains a single integer TT (1≤T≤2001≤T≤200 ), the number of test cases.
For each test case, the first line contains three integers nn (1≤n≤3×1051≤n≤3×105 ), the size of the tree TT , mm (2≤m≤3×1052≤m≤3×105 ), the number of marked paths, and kk (2≤k≤m2≤k≤m ).
The following (n−1)(n−1) lines describe the tree TT . Each of them contains two integers uu and vv (1≤u,v≤n1≤u,v≤n , u≠vu≠v ), representing an edge between the vertices uu and vv .
The following mm lines describe all marked simple paths in the tree. The ii -th of them contains two integers xixi and yiyi (1≤xi,yi≤n1≤xi,yi≤n ).
The input guarantees that the sum of nn and the sum of mm in all test cases are at most 2×1062×106 respectively.
Output
For each test case, output a single line with a single integer, the number of different strategies meeting the requirement modulo (109+7)(109+7) .
Example
Input
Copy
1
3 6 2
1 2
1 3
1 1
2 2
3 3
1 2
1 3
2 3
Output
Copy
10
题目大意 : 输入一棵树, M次操作, 每次在点 U 和 V之间路径的所有点之间连一条边, 从添加的路径当中选择K个路径, 问有多少种方案可以使选中的K个路径的公共点数 ≥ 1
思路 :由于树上两点之间的路径只有一个, 所以可以利用树上差分表示出每个点被经过了多少次, 具体实现为 sum[U]++, sum[V]++, sum[LCA(U, V)]--, sum[fa[LCA(U, V)]]--, 这个不难理解
而答案就是每个点的贡献之和, 贡献的求法为 :, 其中lca【x】表示的是该点作为最近共同祖先所经过的边的数目, 在操作的过程就就可以求得, 实际上就是该点贡献减去父亲的贡献
Accepted code
#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;
#define sc scanf
#define ls rt << 1
#define rs ls | 1
#define Min(x, y) x = min(x, y)
#define Max(x, y) x = max(x, y)
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define MEM(x, b) memset(x, b, sizeof(x))
#define lowbit(x) ((x) & -(x))
#define P2(x) ((x) * (x))
typedef long long ll;
const int MOD = 1e9 + 7;
const int MAXN = 3e5 + 100;
const int INF = 0x3f3f3f3f;
struct Edge
{
int v, next;
}e[MAXN << 1];
int head[MAXN], n, m, k, cnt, T;
int p[MAXN][25], lca[MAXN];
int dep[MAXN], c[MAXN];
ll fac[MAXN], inv[MAXN], pos;
ll pow_mod(ll a, ll b) { // 快速幂取模
ll res = 1;
while (b) {
if (b & 1) res = (res % MOD) * (a % MOD) % MOD;
a = (a % MOD) * (a % MOD) % MOD;
b >>= 1;
}
return res % MOD;
}
void init() {
MEM(head, -1); MEM(p, 0); MEM(dep, 0); MEM(c, 0); MEM(lca, 0);
cnt = pos = 0;
}
ll Cc(ll a, ll b) { // 组合数
if (b == 0 || a < b) return 0;
return fac[a] * inv[b] % MOD * inv[a - b] % MOD;
}
void add(int from, int to) {
e[++cnt].v = to;
e[cnt].next = head[from];
head[from] = cnt;
}
void dfs(int x, int fa) { // 找出倍增关系和每个点的深度
p[x][0] = fa, dep[x] = dep[fa] + 1;
for (int i = 1; (1 << i) <= dep[x]; i++)
p[x][i] = p[p[x][i - 1]][i - 1];
for (int i = head[x]; i != -1; i = e[i].next) {
int vi = e[i].v;
if (vi == fa) continue;
dfs(vi, x);
}
}
int LCA(int x, int y) { // 求最近共同祖先
if (dep[x] > dep[y]) swap(x, y);
for (int i = 20; i >= 0; i--) {
if (dep[y] - (1 << i) >= dep[x])
y = p[y][i];
}
if (x == y) return x;
for (int i = 20; i >= 0; i--) {
if (p[x][i] == p[y][i]) continue;
x = p[x][i], y = p[y][i];
}
return p[x][0];
}
void DFS(int x, int fa) { // 树上差分回溯, 并记录答案, 答案当中有减号,先 + MOD再取模
for (int i = head[x]; i != -1; i = e[i].next) {
int vi = e[i].v;
if (vi == fa) continue;
DFS(vi, x); c[x] += c[vi];
}
pos = ((pos % MOD) + ((Cc(c[x], k) % MOD) - (Cc(c[x] - lca[x], k) % MOD)) + MOD) % MOD;
}
int main()
{
fac[0] = inv[0] = 1;
for (int i = 1; i < MAXN; i++) {
fac[i] = fac[i - 1] * i % MOD;
inv[i] = pow_mod(fac[i], MOD - 2);
}
cin >> T;
while (T--) {
sc("%d %d %d", &n, &m, &k); init();
for (int i = 1; i < n; i++) {
int ui, vi;
sc("%d %d", &ui, &vi);
add(ui, vi); add(vi, ui);
}
dfs(1, 0);
for (int i = 0; i < m; i++) {
int ui, vi;
sc("%d %d", &ui, &vi);
int ans = LCA(ui, vi);
c[ui]++, c[vi]++, c[ans]--, c[p[ans][0]]--;
lca[ans]++;
}
DFS(1, 0);
printf("%lld\n", pos % MOD);
}
return 0;
}