CF1824B2 LuoTianyi and the Floating Islands (Hard Version)
传送门
首先当\(k\)为奇数时,若一个点想为好点,必须他自己就是岛屿之一,所以\(k\)为奇数时答案一定是\(1\)
接着考虑\(k\)为偶数时怎么求好点数。我们发现,若一个点为好点,则当其延某一条边移动一次得到的点的答案一定不能更优,否则这次移动得到的点才是好点。也就是说,对于好点所连的所有边,好点这一侧的岛屿数量必须大于等于另一侧的岛屿数量,也就说,我们可以算一个点在多少个方案中作为好点出现,出现的次数即\(C_{n}^{k}-\sum_{v} \sum_{j=\frac{k}{2} +1}^{min(k,siz_v)} C_{siz_v}^{j}\)
但是这样统计答案是\(O(n^2)\)的复杂度。
注意到所有的好点一定是连续出现的,即所有的好点一定组成了一个连通块,而这个连通块一定是树。所以我们可以定义好边为其两侧的岛屿数量相同的边,这样好点的数量就是好边的数量加\(1\),因为树中点的数量为边的数量加\(1\)
而好边的数量是好算的,即\(\sum C_{siz_u}^{\frac{k}{2}} C_{siz_v}^{\frac{k}{2}}\)
最后输出答案记得除以\(C_{n}^{k}\)
当某种点的数量/贡献难求时,可以考虑转化为求边的数量/贡献
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5e5 + 10;
const int inf = 1e18 + 10;
const int mod = 1e9 + 7;
int n,m,fac[N],inv[N],siz[N],ans;
vector<int> g[N];
int quickMul(int x,int k) {
int res = 1;
while(k) {
if(k & 1) res = res * x % mod;
x = x * x % mod;
k >>= 1;
}
return res;
}
int C(int n,int m) {
return fac[n] * inv[m] % mod * inv[n - m] % mod;
}
void dfs(int u,int fa) {
siz[u] = 1;
for(auto i : g[u]) {
if(i == fa) continue;
dfs(i,u);
siz[u] += siz[i];
if(siz[i] >= m / 2 && n - siz[i] >= m / 2) ans = (ans + C(siz[i],m / 2) * C(n - siz[i],m / 2) % mod) % mod;
}
// if(n - siz[u] >= m / 2 + 1) ans = (ans + get_num(n - siz[u])) % mod;
}
void solve() {
cin >> n >> m;
for(int u,v,i = 1;i < n;i++) {
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
if(m & 1) return (void)(cout << 1 << '\n');
dfs(1,0);
// ans = ((n * C(n,m) % mod - ans) % mod + mod) % mod;
// cout << ans << ' ' << C(n,m) << '\n';
cout << (ans * quickMul(C(n,m),mod - 2) % mod + 1) % mod << '\n';
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
fac[0] = 1;
for(int i = 1;i < N;i++) fac[i] = fac[i - 1] * i % mod;
inv[N - 1] = quickMul(fac[N - 1],mod - 2);
for(int i = N - 2;i >= 0;i--) inv[i] = inv[i + 1] * (i + 1) % mod;
int t = 1;
while(t--) solve();
return 0;
}

浙公网安备 33010602011771号