2023“钉耙编程”联赛 Day 1 L 题题解 Play on Tree

首先我们考虑树根固定为 u u u 的情况的 SG \text {SG} SG 值怎么求,发现可以对于每条与孩子的链接分别考虑再异或起来。

用小粉兔的话说就是将树根复制多份,使问题变成多个游戏,每个游戏保证树根只有一个孩子。

对于每个链接,我们可以选子树的边或者链接的边,如果是选链接的边,则转移到 SG \text {SG} SG 值为 0 0 0 的状态。

否则,我们选子树里的边,设儿子的 SG \text {SG} SG 值为 SG v \text{SG}_v SGv,则断了边后子树的 SG \text {SG} SG 值肯定会变化为 [ 0 , SG v − 1 ] [0,\text {SG}_v-1] [0,SGv1] 的某一项。

我们只能猜一手结论。

我们用个数学归纳法,设子树 SG \text {SG} SG 值为 [ 1 , x − 1 ] [1,x-1] [1,x1] 时满足 SG u = SG v + 1 \text {SG}_u=\text {SG}_v+1 SGu=SGv+1

如果 SG \text{SG} SG 值为 0 0 0 就是只可以断链接边的情况,得 SG \text{SG} SG 值为 1 1 1,说明在边界我们的假设成立。

对于 SG \text{SG} SG 值为 x x x 的情况,我们发现我们现在断边之后,对于子树的 SG \text{SG} SG 值,我们必然会转换成 [ 0 , x − 1 ] [0,x-1] [0,x1] 其中的一种情况,根据前面的假设,我们转移到了 [ 1 , x ] [1,x] [1,x] 的状态。

由于 0 0 0 是存在的,所以 SG u = SG v + 1 \text {SG}_u=\text {SG}_v+1 SGu=SGv+1

于是按上文所述异或起来即可。

得公式:

SG u = ⨁ v ∈ u s o n ( SG v + 1 ) \text {SG}_u=\bigoplus_{v\in uson}(\text {SG}_v+1) SGu=vuson(SGv+1)

在本题考虑换根的情况并不是很难,可以自己画图理解一手,发现我们转移到点 v v v 时,相当于点 v v v 多了一个儿子 u u u,而且 u u u 少了一个儿子 v v v

设新的 SG \text {SG} SG 值为函数 SG \text {SG} SG,旧的为 SG’ \text {SG'} SG’

SG’ v = SG’ v ⊕ ( SG u + 1 ) = SG’ v ⊕ ( ( SG’ u ⊕ ( SG’ v + 1 ) ) + 1 ) \begin{aligned} \text {SG'}_v&=\text {SG'}_v\oplus (\text {SG} u+1)\\ &=\text {SG'}_v\oplus ((\text {SG'}u\oplus (\text {SG'}_v+1))+1) \end{aligned} SG’v=SG’v(SGu+1)=SG’v((SG’u(SG’v+1))+1)

注意运算优先级。

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const LL N=6e5+5;
const LL mod=1e9+7;
LL T,n,x,y,sg[N],ans[N];
vector<LL>v[N];
void dfs(LL x,LL f)
{
	for(LL i:v[x])
	{
		if(i==f)continue;
		dfs(i,x);
		sg[x]^=sg[i]+1;
	}
}
void dfs2(LL x,LL f)
{
	for(LL i:v[x])
	{
		if(i==f)continue;
		ans[i]=sg[i]^((ans[x]^(sg[i]+1ll))+1ll);
		dfs2(i,x);
	}
}
LL ksm(LL x,LL y)
{
	LL ans=1;
	while(y)
	{
		if(y&1)ans=ans*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return ans;
}
int main()
{
	scanf("%lld",&T);
	while(T--)
	{
		memset(v,0,sizeof(v));
		memset(sg,0,sizeof(sg));
		memset(ans,0,sizeof(ans));
		scanf("%lld",&n);
		for(int i=1;i<=n-1;i++)
		{
			scanf("%lld%lld",&x,&y);
			v[x].push_back(y);
			v[y].push_back(x);
		}
		dfs(1,0);
		ans[1]=sg[1];
		dfs2(1,0);
		LL cnt=0;
		for(int i=1;i<=n;i++)
		{
			if(ans[i]!=0)cnt++;
		}
		LL sum=cnt*ksm(n,mod-2)%mod;
		printf("%lld\n",sum);
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值