CF1369D TediousLee 题解(树形DP+递推)

题目链接

由于本题的取模会影响最大值的判断,本题解并非正解,仅在不取模的情况下才能保证答案正确。但因为数据太水,本方法还是可以AC。仅供参考

个人认为是一道非常有意思的题,题意就不赘述了

首先,要发现这题的做法,必须要先打表找规律。可以自己在纸上用手捏出 n ≤ 6 n\le 6 n6 的数据

经过打表后,我们可以发现下面这个规律:

image.png

图稍微有点丑,见谅

这里其实很像树的最大独立集,我们可以用树形DP实现
f [ x ] [ 0 ] f[x][0] f[x][0] 为以 x x x 为根的子树中,不选 x x x 的情况下可以得到的最多的 claw 的数量。注意这里是 claw 的数量,不是染成黄色的节点数
同理,设 f [ x ] [ 1 ] f[x][1] f[x][1] 为选 x x x 的情况下可以得到的最多的 claw 的数量。那么,转移方程就是:
f [ x ] [ 1 ] = ∑ f [ y ] [ 0 ] f[x][1]=\sum f[y][0] f[x][1]=f[y][0] f [ x ] [ 0 ] = ∑ max ⁡ ( f [ y ] [ 1 ] , f [ y ] [ 0 ] ) f[x][0]=\sum \max(f[y][1],f[y][0]) f[x][0]=max(f[y][1],f[y][0]),其中 y y y x x x 的儿子

但是, T ≤ 1 0 4 , n ≤ 2 ⋅ 1 0 6 T\le 10^4,n\le 2 \cdot 10^6 T104,n2106 的数据范围并不允许。

根据上面的打表,相信你们一定会发现每一个级别对应着一个唯一的 RDB,又因为 n ≤ 2 ⋅ 1 0 6 n\le 2 \cdot 10^6 n2106,所以我们可以用递推或者记忆化搜索实现(下面的 f f f 跟上面的没有关系)

f [ i ] [ 1 ] f[i][1] f[i][1] 为在级别为 i i i RDB 中,如果选上根节点时最多可以选出多少个 claw
同理 f [ i ] [ 0 ] f[i][0] f[i][0] 表示不选根节点时最多可以选出的 claw 的个数
转移方程: f [ i ] [ 1 ] = 2 × f [ i − 2 ] [ 0 ] + f [ i − 1 ] [ 0 ] + 1 f[i][1]=2 \times f[i-2][0]+f[i-1][0]+1 f[i][1]=2×f[i2][0]+f[i1][0]+1
f [ i ] [ 0 ] = 2 × max ⁡ ( f [ i − 2 ] [ 0 ] , f [ i − 2 ] [ 1 ] ) + max ⁡ ( f [ i − 1 ] [ 0 ] , f [ i − 1 ] [ 1 ] ) f[i][0]=2 \times \max(f[i-2][0],f[i-2][1])+\max(f[i-1][0],f[i-1][1]) f[i][0]=2×max(f[i2][0],f[i2][1])+max(f[i1][0],f[i1][1])

时间复杂度 O ⁡ ( n ) \operatorname{O}(n) O(n)

#include<cstdio>
#include<iostream>
using namespace std;
const int Maxn=2000000+10,mod=1000000007;
int f[Maxn][2];
int n,m;
inline int read()
{
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0' && ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
	return s*w;
}
int main()
{
//	freopen("in.txt","r",stdin);
	int T=read();
	m=2;
	while(T--)
	{
		n=read();
		if(m<n)
		for(int i=m+1;i<=n;++i)
		{
			f[i][1]=((f[i-2][0]<<1)%mod+f[i-1][0]+1)%mod;
			int tmp=(max(f[i-2][0],f[i-2][1])<<1)%mod;
			f[i][0]=(tmp+max(f[i-1][0],f[i-1][1]))%mod;
		}
		m=max(n,m);
		int ans=max(f[n][0],f[n][1]);
		ans=(ans<<1)%mod;
		ans=(ans<<1)%mod; // 输出的时候记得乘以4!
		printf("%d\n",ans);
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值