Codeforces Round 872 (Div. 2)

\sqrt( )Problem - D2 - Codeforces

思路:

我们设good点到所有k点的距离和为dis。

  1. 假设good点不止一个,那么我们good点的dis应该都是相等的(废话)
  2. 设当前点u是good点,如果他往儿子v移动,儿子有w个点属于k,那么v的距离和是dis-w+(k-w)=dis+k-2w(即往儿子走,儿子子树里w个点到当前点的距离减一,子树外面的点距离+1)
  3. 因为u是good点,所以k-2w>=0
    1. 如果存在其他good点,我们显然每次走都应该是等号成立
    2. 你会发现,k为奇数时k/2!=w。所以k为奇数,显然只有应该good点,任何分布情况都是k为1.
    3. 当满足k/2=w,即v的子树有k/2个点,子树外面有k/2个点,便是good点。
    4. 发现每次移动都是等号成立,即good点应该是连成一片的。(而且我们保证,任何分布情况至少2个good点。因为那两颗有k/2个点是目标点的子树,他们的根节点必然就是good点)
    5. 我们dfs确认符合思路3的边(即good之间的边)。那么符合条件的点就是符合的边+1
    6. 为什么不直接算点呢?
      1. 如上图,我们从1遍历,2,3显然是good点,但是这样从根节点遍历2却不是,因为他的子树是(2-3-4),有3个点,而如果他的子树是(2-1),那么就是good点。
      2. 即我们dfs实际遍历的是符合条件的点之间的边(如这里成功计算的就是2-3的边)。
      3. 所以我们任何分布情况都计算了一个点
    7. 那么答案就是(ΣC(sz[v],k/2)*C(n-sz[v],k/2)/C(n,k) )+1,即每个点的期望就是,所以可能的分布C(n,k)下,子树内部选k/2个点,外面选k/2个点的情况,最后加上任何情况都漏下的一个点
#include <bits/stdc++.h>
using namespace std;
#define ll               long long
#define endl             "\n"
#define int              long long
const int N = 3e5 + 10;
const int mod=1e9+7;
int n,k;
vector<int>edge[N];
int pre[N],inv[N],sz[N];
ll ans;
ll fastmi(ll base,ll power)
{
	ll ans=1;
	while(power)
		{
			if(power&1)ans=ans*base%mod;
			base=base*base%mod;
			power>>=1;
		}
	return ans;
}

int C(int n,int m)
{
	if(n<0||m<0||n<m)return 0;
	return pre[n]*inv[m]%mod*inv[n-m]%mod;//取模运算不允许出现除法,所以除数都转化为逆元存下来
}

void dfs(int u,int f)
{
	sz[u]=1;
	for(auto v:edge[u])if(v!=f)
			{
				dfs(v,u);
				sz[u]+=sz[v];
				ans=(ans+C(sz[v],k/2)*C(n-sz[v],k/2)%mod)%mod;//计算子树v的贡献
			}
}

void mysolve()
{
	cin>>n>>k;
	int x,y;
	for(int i=1; i<n; ++i)cin>>x>>y,edge[x].push_back(y),edge[y].push_back(x);
	if(k&1)
		{
			cout<<1<<endl;
			return;
		}
	ans=C(n,k);//即+1
	dfs(1,0);
	ans=ans*fastmi(C(n,k),mod-2)%mod;
	cout<<ans<<endl;
	return;
}

int32_t main()
{
	std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	ll t=1;
	//cin >> t;
	pre[0]=1;
	for(int i=1; i<=2e5; ++i)pre[i]=pre[i-1]*i%mod;
	inv[200000]=fastmi(pre[200000],mod-2);
	for(int i=2e5-1; ~i; --i)inv[i]=inv[i+1]*(i+1)%mod;
	//for(int i=)
	while (t--)
		{
			mysolve();
		}
	system("pause");
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值