超级神题!
有n种字符,若此种字符的编号( (1) ~ (n)),(i*2>n),则他后面可接任意字符。若不是,则他后面接的字符编号至少要是他的两倍。
问长度为m的字符串的个数。
这道题我只想出了 (O(n^2)) 的做法,于是叕只能求助题解。
题解的做法和周六第二题有点像,但我并没有分析出最长链的长度小于 (log_{2}n) 这个性质。知道这个性质之后,就可以做了。
设 (f[i]) 表示长度为 (i) 的合法字符串的个数。(g[i]) 表示长度为 (i) 的合法链的个数。
转移就是
[f[i]=sum_{j=1}^{maxlen}g[j]*f[i-j]
]
但 (g[i]) 并不能直接转移,所以要设个辅助状态。
设 (p[i][j]) 表示以 (j) 结尾,长度为 (i) 的链的个数,(g[i]) 就是 (p[i]) 里合法链的和。
[p[i][j]=sum_{k=1}^{j/2}p[i-1][k]
]
这个可以用前缀和优化,也可以将式子化简为从 (p[i][j-1]) 转移过来。
[p[i][j]=p[i][j-1]+p[i-1][j/2]*(j mod 2==0)
]
总复杂度 (O(m*log_{2}n))。
#include <bits/stdc++.h>
using namespace std;
#define db double
#define ll long long
#define RG register
inline int gi()
{
RG int ret; RG bool flag; RG char ch;
ret=0, flag=true, ch=getchar();
while (ch < '0' || ch > '9')
ch == '-' ? flag=false : 0, ch=getchar();
while (ch >= '0' && ch <= '9')
ret=(ret<<3)+(ret<<1)+ch-'0', ch=getchar();
return flag ? ret : -ret;
}
const db pi = acos(-1.0);
const int N = 1e6+5, mod = 1e9+7;
int f[N],g[N],p[22][N];
int main()
{
RG int n,m,len,i,j;
n=gi(), m=gi();
len=log2(n)+2;
p[1][1]=1, f[0]=1;
for (i=1; i<=len; ++i)
{
for (j=1; j<=n; ++j)
{
(p[i][j]+=p[i][j-1])%=mod;
if (j<<1 <= n)
(p[i+1][j<<1]+=p[i][j])%=mod;
else
(g[i]+=p[i][j])%=mod;
}
}
for (i=1; i<=m; ++i)
for (j=1; j<=len && j<=i; ++j)
(f[i]+=((ll)f[i-j]*g[j])%mod)%=mod;
printf("%d
",f[m]);
return 0;
}