51nod【1196】字符串的数量

超级神题!
有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\n",f[m]);
    return 0;
}

转载于:https://www.cnblogs.com/y142857/p/7544962.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值