给一棵树,从任意起点出发,到路径末端时,经过“魔法点”的个数为偶数的方案数和。
我的解法
如果考虑某一个点,很容易写出转移方程,设
f
[
x
]
[
0
/
1
]
f[x][0/1]\quad
f[x][0/1]=x的所有子儿子是偶数/奇数的方案数数
f
[
x
]
[
0
]
=
∏
y
=
s
o
n
[
x
]
f
[
y
]
[
0
]
+
∏
y
=
s
o
n
[
x
]
f
[
y
]
[
1
]
f
[
x
]
[
1
]
=
∏
y
=
s
o
n
[
x
]
f
[
y
]
[
1
]
+
∏
y
=
s
o
n
[
x
]
f
[
y
]
[
0
]
f[x][0] = \prod_{y=son[x]}{f[y][0]} + \prod_{y=son[x]}{f[y][1]} \\ f[x][1] = \prod_{y=son[x]}{f[y][1]} + \prod_{y=son[x]}{f[y][0]}
f[x][0]=y=son[x]∏f[y][0]+y=son[x]∏f[y][1]f[x][1]=y=son[x]∏f[y][1]+y=son[x]∏f[y][0]
发现
f
[
x
]
[
0
]
=
f
[
x
]
[
1
]
f[x][0] = f[x][1]
f[x][0]=f[x][1],可以简化
f
[
x
]
=
2
⋅
∏
y
=
s
o
n
[
x
]
f
[
y
]
f[x] = 2\cdot \prod_{y=son[x]}{f[y]}
f[x]=2⋅y=son[x]∏f[y]
进而又发现f是2的幂次,取log再改写
g [ x ] = 1 + ∑ y = s o n [ x ] g [ y ] g[x] = 1+\sum_{y=son[x]}{g[y]} g[x]=1+y=son[x]∑g[y]
显然不能一个个枚举根然后再计算,所以考虑每个点对全局的贡献。
分为叶子和非叶子观察。
非叶节点的+1是解决问题的关键,如果根在x的一端,那么x的+1是会传递过去的;如果根在x的另一端,+1也会传递过去。也就是说+1是传递遍全图的。所以非叶节点的方案数就是: 2 ∣ 非叶结点 ∣ 2^{|非叶结点|} 2∣非叶结点∣。
叶结点发现他自己的+1能对自己生效,所以多一个1, 2 ∣ 非叶节点 ∣ + 1 2^{|非叶节点| + 1} 2∣非叶节点∣+1。
记叶子有leaf个,非叶子nleaf,问题答案
a
n
s
=
n
l
e
a
f
⋅
2
n
l
e
a
f
+
l
e
a
f
⋅
2
n
l
e
a
f
+
1
ans = nleaf \cdot 2^{nleaf} + leaf \cdot 2^{nleaf + 1}
ans=nleaf⋅2nleaf+leaf⋅2nleaf+1
别人的解法
记叶子节点数为 L L L,非叶子节点数位 N N N。
对于非叶子节点任意选取的情况下,叶子节点有唯一的选取方式使其成立。所以所有非叶节点的方案数是 N ⋅ 2 N N\cdot 2^N N⋅2N;
对于叶子节点,自己的选取方式也可以任意选取了,所以所有叶结点的方案数 L ⋅ 2 N + 1 L\cdot 2^{N+1} L⋅2N+1;
答案是一样的:
a
n
s
=
N
⋅
2
N
+
L
⋅
2
N
+
1
ans = N\cdot 2^N + L\cdot 2^{N+1}
ans=N⋅2N+L⋅2N+1
我的错误点(略过即可)
最后的方案数是要所有加起来的,当时脑子里全想着取了log,所以加法是乘法,应该把所有方案数乘起来,是错误的。
#include<bits/stdc++.h>
#define debug(x) cerr << #x << " " << x << endl
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
const int N = 1e5 + 10;
int n, deg[N], leaf = 0;
ll qkpow(ll a, ll n)
{
n %= mod - 1;
ll ret = 1;
while(n)
{
if(n & 1) ret = ret * a % mod;
a = a * a % mod;
n >>= 1;
}
return ret;
}
int main()
{
scanf("%d", &n);
for(int i = 1; i < n; i++)
{
int x, y;
scanf("%d%d", &x, &y);
deg[x]++;
deg[y]++;
}
for(int i = 1; i <= n; i++)
{
if(deg[i] == 1) leaf++;
}
int nleaf = n - leaf;
debug(leaf);
ll ans1 = leaf * qkpow(2, nleaf + 1) % mod;
ll ans2 = nleaf * qkpow(2, nleaf) % mod;
printf("%lld\n", (ans1 + ans2) % mod);
}