文章目录
G. Rikka with Intersections of Paths
题目大意
多组输入开始一个数字 T 表示有 T 组输入
给你一棵有 n 个节点的树,然后这棵树上有m条路被标记了,问你从这m条标记的路中选取k条,其中这k条最少有一个公共点,一共有多少种选法,最后的答案数对1e9+7取余;
比如说样例这组数据 我们可以选择 2 3 这条路然后随便选择任何一条路都可以,因为从2到3这条路已经包涵了所有节点,所以在选择其他任何被标记的路都可以;
Example
input
1
3 6 2
1 2
1 3
1 1
2 2
3 3
1 2
1 3
2 3
output
10
解题思路
既然选择的两条路有至少一个相同点,那说明这两条路径肯定有一个公共祖先,然后我们记录每个点被经过几次和每个点作为一条路的祖先被经过几次;然后我们就可以枚举每个节点,设这个节点一共被经过了 a 次,作为祖先节点被经过 b 次,那么以这个节点作为k标记的路的重复节点的选法一共有C(a,k)-C(a-b,k)种方案;这是问什么呢?因为一共经过他的边有 a 条,然后这a 条边任意算k条就行,但是如果这样选,会有重复的选择;所以我们需要选择一个不会有重复的策略,因为我们知道任意一条边都会有一个祖先,所以让这个节点作为祖先被经过的次数来表示这几条边,这样这几条边就有一个唯一的标识度了,我们可以再这几条边中选择若干条,然后剩下的从不是以这个节点作祖先的边中选着,就可以;这样就不会选择重复了;这样的选法就是上面说的C(a,k)-C(a-b,k);这个可以类似于现在有黑球n个,红球m个让你选择出来k个球,不能全是红球的组合公式,用总数减去不合法的数量;
现在主要的思路有了,那么如果求一个节点被经过了几次呢?
我们可以用差分的思想了求,从树的下面开始向上叠加就行;
比如说又一条 x 到 y 的边我们在记录的时候只需要
设 f=lca(x,y), fa为 f 的父亲节点
c[x]++,c[y]++,c[f] --,c[fa] --; ;
这条路上的祖先节点就直接让 lca[f]++就可以了;
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int mx=300300;
const ll mod=1e9+7;
int ans;
int T;
int n,m,k;
vector<int>ve[mx];//存图
ll fac[mx],inv[mx];//用于求组合函数
ll c[mx],lca[mx];
//c 表示这个点被经过几次,lca表示这个点作为祖先别经过的次数
int de[mx],fa[mx][30];//标准 Lca的数字
int bit[30];//预处理2的n次方用的
ll poow(ll a,ll b)//快速幂求 逆元
{
ll ans=1;
while(b)
{