首先我们考虑树根固定为 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,SGv−1] 的某一项。
我们只能猜一手结论。
我们用个数学归纳法,设子树 SG \text {SG} SG 值为 [ 1 , x − 1 ] [1,x-1] [1,x−1] 时满足 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,x−1] 其中的一种情况,根据前面的假设,我们转移到了 [ 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=v∈uson⨁(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);
}
}