由于本题的取模会影响最大值的判断,本题解并非正解,仅在不取模的情况下才能保证答案正确。但因为数据太水,本方法还是可以AC。仅供参考
个人认为是一道非常有意思的题,题意就不赘述了
首先,要发现这题的做法,必须要先打表找规律。可以自己在纸上用手捏出 n ≤ 6 n\le 6 n≤6 的数据
经过打表后,我们可以发现下面这个规律:
图稍微有点丑,见谅
这里其实很像树的最大独立集,我们可以用树形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 T≤104,n≤2⋅106 的数据范围并不允许。
根据上面的打表,相信你们一定会发现每一个级别对应着一个唯一的 RDB
,又因为
n
≤
2
⋅
1
0
6
n\le 2 \cdot 10^6
n≤2⋅106,所以我们可以用递推或者记忆化搜索实现(下面的
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[i−2][0]+f[i−1][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[i−2][0],f[i−2][1])+max(f[i−1][0],f[i−1][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;
}