小明正在一棵树上数星星,这棵树有 n 个结点 1,2,⋯,n。他定义树上的一个子图 G 是一颗星星,当且仅当 G 同时满足:
- G 是一棵树。
- G 中存在某个结点,其度数为 ∣VG∣−1。其中 ∣VG∣ 表示这个子图含有的结点数。
两颗星星不相同当且仅当它们包含的结点集合 VG 不完全相同。小明想知道这棵树上有多少颗不同的星星包含的结点的数量在区间 [L,R] 中,答案对 1000000007 取模。
输入格式
输入共 n+1 行。
第一行为一个正整数 n。
后面 n−1 行,每行两个正整数表示树上的一条边。
第 n+1 行,两个正整数 L,R。
输出格式
输出共 1 行,一个整数表示答案。
输入输出样例
输入 #1复制
6 1 2 1 3 2 4 2 5 3 6 3 4
输出 #1复制
6
说明/提示
【样例说明】
包含 3 个结点的星星有 5 个,它们的结点集合分别为 {1,2,3},{1,2,4},{1,2,5},{2,4,5},{1,3,6}。
包含 4 个结点的星星有 1 个,它的结点集合为 {1,2,4,5}。
思路:
首先要是一个子图 G 是一棵树,且存在一个节点的度数为 ∣VG∣−1,也就是子图节点数量减一,显然的,这棵树的层数必须为 2,从另一个方面说,也就是一棵由一个节点,我们可以把它看成根,它连接了剩余 ∣VG∣−1 个节点,且剩余节点之间没有连边,那么我们明白了要想产生一颗星星他所需要的条件:
-
节点数量在范围之内。
-
我们设子图节点数量为 x,那么它的连边数量为 x−1,这也就是树的基本特性。
-
子图看作一棵树只有两层。
-
存在一个节点使得它连向了剩余的所有节点,且剩余节点之间没有连边。
知道了这些就好做了,我们去遍历一棵树,每遍历到一个节点,我们就去算以他为根形成满足条件的子图数量即可,那么怎样计算满足条件的子图数量呢?现在我们设当前子图节点数量为 x,首先我们从他给定的范围开始,方案也就是从 x−1 个节点中选 l 个,从 x−1 个节点中选 l+1 个,从 x−1 个节点中选 l+2 个,一直到从 x−1 个节点中选 r 个,将选择的不同数量的相应方案数相加即可,但为什么 x 要减去 1 呢?因为要去掉根,因为根是目前连接剩余 x−1 个节点的,不能算入进去。这一步,我们可以用组合数计算出,提前与处理好阶乘和逆元,可以更快更方便的计算出以节点 i 为根产生的贡献。
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 1e9 + 7;
int n, in[100005], inv[100005], fec[100005], f[100005], l, r, res;
vector < int > g[100005];
int C (int x, int y)
{
return inv[x] * f[y] % mod * f[x - y] % mod;
}
void dfs (int x, int fa)
{
for (auto y : g[x])
{
if (y ^ fa)
dfs(y, x);
}
if (static_cast<int>(g[x].size()) + 1 >= l)
{
int p = min(r - 1ll, static_cast<int>(g[x].size()));
for (int i = l - 1;i <= p; ++ i)
{
res = (res + C(g[x].size(), i) % mod + mod) % mod;
}
}
}
signed main()
{
cin >> n;
inv[0] = fec[0] = inv[1] = fec[1] = f[0] = f[1] = 1;
for (register int i = 2;i <= n; ++ i)
{
inv[i] = inv[i - 1] * i % mod;
fec[i] = fec[mod % i] * (mod - mod / i) % mod;
f[i] = f[i - 1] * fec[i] % mod;
}
for (register int i = 1;i < n; ++ i)
{
int x, y;
cin >> x >> y;
g[x].push_back(y);
g[y].push_back(x);
}
cin >> l >> r;
dfs(1, 0);
cout << res << "\n";
return 0;
}
AC截图: