【题目描述】
【思路】
做不出来,看了讨论区大佬的题解才写出来的。
这道题是V1难度,还有V2,V3根本不会,先贴上V1的题解
下面的所有字母编号都从
1
1
1 开始,范围
[
1
,
n
]
[1,n]
[1,n]
首先,一个合法的字符串显然是由若干个合法的“链”组成的。链的定义就是:从一个字母开始连,后面每个字母编号必须大于等于前一个的2倍,这样尽可能的连接下去。所谓尽可能连接下去的意思是,链的最后一个字母编号i必须满足
2
∗
i
>
n
2 * i > n
2∗i>n ,这样后面不能接东西了,并且只有这样的链才合法。对于每个合法字符串,划分成合法链的方法是唯一的。
比如
N
=
2
M
=
3
N = 2 M = 3
N=2M=3 的
3
3
3 个解 分别是
(
a
b
)
(
b
)
,
(
b
)
(
a
b
)
,
(
b
)
(
b
)
(
b
)
(ab)(b), (b)(ab), (b)(b)(b)
(ab)(b),(b)(ab),(b)(b)(b)
问题转化为两步:
(1)
g
(
x
)
g(x)
g(x) 表示长度为x的合法链的个数,求
g
(
x
)
g(x)
g(x)
(2)
v
(
x
)
v(x)
v(x) 表示长度为x的合法字符串数,求
v
(
x
)
v(x)
v(x)
对于(2) 显然我们有
v
(
x
)
=
g
(
1
)
∗
v
(
x
−
1
)
+
g
(
2
)
∗
v
(
x
−
2
)
+
.
.
.
+
g
(
p
)
∗
v
(
x
−
p
)
v(x) = g(1) * v(x - 1) + g(2) * v(x - 2) +...+g(p) * v(x - p)
v(x)=g(1)∗v(x−1)+g(2)∗v(x−2)+...+g(p)∗v(x−p)
就是长度为x的解可以先弄出一条链来,再构造剩余的部分。为方便可以定义
v
(
0
)
=
1
v(0) = 1
v(0)=1,这样单独一条长度为x的合法链也是合法解。 其中p是最长的合法链的长度。
用这个式子直接推长度为
m
m
m 的结果复杂度是
O
(
m
∗
p
)
O(m * p)
O(m∗p)
显然,当有n种字母的时候,
p
p
p 是
O
(
l
o
g
n
)
O(logn)
O(logn) 级别的。所以这部分复杂度是
O
(
m
∗
l
o
g
n
)
O(m*logn)
O(m∗logn) 在
m
,
n
m,n
m,n 比较小的时候这部分复杂度可以了。
然后上面的题解只给了 v ( x ) v(x) v(x) 的递推式,但在这之前还要计算出所有的 g ( x ) g(x) g(x),这个我是又用了一个 d p dp dp 来求解的,如果设 d p ( i , j ) dp(i,j) dp(i,j) 表示长度为 i i i 的序列 ,每一项都在 [ 1 , n ] [1,n] [1,n] 中取,同时严格满足 a x ∗ 2 < = a x + 1 a_x*2<=a_{x+1} ax∗2<=ax+1 对应的序列的数量,那么有递推式 d p ( i , j ) = ∑ k = 1 ⌊ j / 2 ⌋ d p ( i − 1 , k ) ( i < = l o g n ) dp(i,j)=\sum_{k=1}^{\lfloor j/2 \rfloor}dp(i-1,k) \ \ \ \ (i<=logn) dp(i,j)=k=1∑⌊j/2⌋dp(i−1,k) (i<=logn) 可以利用前缀和优化,最终在 O ( n ∗ l o g n ) O(n*logn) O(n∗logn) 求解出 d p dp dp,然后可以根据 d p dp dp 数组的值求出 g g g,然后再按照题解那样递推答案
#include<bits/stdc++.h>
#define min(a,b)(a<b?a:b)
using namespace std;
const int maxn=1000005;
const int mod=1e9+7;
int n,m,logn=1;
int dp[30][maxn],g[30];
int ans[maxn];
int main(){
scanf("%d%d",&n,&m);
while((1<<(logn))<=n) ++logn;
for(int j=1;j<=n;++j) dp[1][j]=dp[1][j-1]+1;
for(int i=2;i<=logn;++i){
for(int j=(1<<(i-1));j<=n;++j){
dp[i][j]=dp[i-1][j/2];//先求出长度为i,以j为末尾的序列数量
}
for(int j=(1<<(i-1));j<=n;++j){
dp[i][j]=(dp[i][j]+dp[i][j-1])%mod;//最后再算一下前缀和
}
}
for(int i=1;i<=logn;++i) g[i]=((dp[i][n]-dp[i][n/2])%mod+mod)%mod;
ans[0]=1;
ans[1]=n-n/2;
for(int i=2;i<=m;++i){
for(int j=1;j<=min(i,logn);++j){
ans[i]=(ans[i]+(long long)ans[i-j]*g[j]%mod)%mod;
}
}
printf("%d\n",ans[m]);
return 0;
}