CF852E Casinos and travel(数学)

这篇博客讨论了一种树形结构问题,涉及从任意起点到终点,路径上经过“魔法点”个数为偶数的方案数。作者提出了动态规划的解法,并通过简化转移方程得出最终方案数为2的幂次。错误点在于将加法误解为乘法。另一位解法则分别考虑了叶子节点和非叶子节点的贡献,同样得到了相同的答案。最后,博主反思了错误并给出了修正后的代码。
摘要由CSDN通过智能技术生成

给一棵树,从任意起点出发,到路径末端时,经过“魔法点”的个数为偶数的方案数和。

我的解法

如果考虑某一个点,很容易写出转移方程,设 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]=2y=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=nleaf2nleaf+leaf2nleaf+1


别人的解法

记叶子节点数为 L L L,非叶子节点数位 N N N

对于非叶子节点任意选取的情况下,叶子节点有唯一的选取方式使其成立。所以所有非叶节点的方案数是 N ⋅ 2 N N\cdot 2^N N2N

对于叶子节点,自己的选取方式也可以任意选取了,所以所有叶结点的方案数 L ⋅ 2 N + 1 L\cdot 2^{N+1} L2N+1

答案是一样的:
a n s = N ⋅ 2 N + L ⋅ 2 N + 1 ans = N\cdot 2^N + L\cdot 2^{N+1} ans=N2N+L2N+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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值