codeforces 585F. Digits of Number Pi

26 篇文章 0 订阅
4 篇文章 0 订阅

AC自动机和数位dp都写挂了….我是不是该退役了…

问L~R有多少个数满足数字里包含一个长度至少为d/2的给定串的子串

如果包含了一个长度大于d/2的子串,也一定包含长度为d/2的子串
所以相当于问L~R有多少个数满足数字里包含一个长度为d/2的给定串的子串
我们把原串所有长度为d/2的子串建到AC自动机上
因为d<=50,所以点数最大不超过25000(实际上最大值应该也比这个小很多)
所以就可以直接数位dp了
f[i][j][k][x]表示dp到第i位,前面是否有前导0,前面是否顶格,走到AC自动机上的x节点的方案数
本来还要加一维表示是否已经有长度为d/2的子串了,但是我灵机一动,每个长度为d/2的子串的结束节点的所有孩子都指向他自己,就可以省掉这一维了

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxp = 110000;
const int maxn = 1100;
const ll Mod = 1e9+7;

struct trie
{
    int son[10],fail;
}tr[maxp]; int root,tot;
bool v[maxp];
void ins(char s[],const int l,const int r)
{
    int x=root;
    for(int i=l;i<r;i++)
    {
        const int w=s[i]-'0';
        int &y=tr[x].son[w];
        if(!y) y=++tot;
        x=y;
    }
    v[x]=true;
}
char str[maxn]; int n,d;
queue<int>q;
void build()
{
    n=strlen(str); int D=d/2;
    root=tot=0;
    for(int i=0;i+D-1<n;i++) ins(str,i,i+D);
    q.push(root);
    while(!q.empty())
    {
        const int x=q.front(); q.pop();
        if(v[x]) for(int i=0;i<10;i++) tr[x].son[i]=x;
        else
        {
            for(int i=0;i<10;i++)
            {
                int &y=tr[x].son[i],fl=tr[x].fail;
                if(y) 
                {
                    if(x!=root) tr[y].fail=tr[fl].son[i];
                    q.push(y);
                }
                else y=tr[fl].son[i];
            }
        }
    }
}

char A[maxn],B[maxn];
//  rol 0 up
ll f[2][2][2][maxp];
ll cal(char s[])
{
    int now=0; 
    memset(f,0,sizeof f); f[now][1][1][0]=1;
    for(int p=0;p<d;p++)
    {
        now=!now;
        for(int i=0;i<2;i++) for(int j=0;j<2;j++)
        {
            for(int x=0;x<=tot;x++) if(f[!now][i][j][x])
            {
                ll tmp=f[!now][i][j][x]%Mod; f[!now][i][j][x]=0;
                int uk=j?s[p]-'0':9;
                for(int k=0;k<=uk;k++)
                {
                    int ni=i&(k>0?0:1),nj=j&(k==uk?1:0),y=(!k&&i)?0:tr[x].son[k];
                    f[now][ni][nj][y]+=tmp;
                }
            }
        }
    }
    ll re=0;
    for(int i=0;i<2;i++) for(int j=0;j<2;j++) for(int x=0;x<=tot;x++) if(v[x]&&f[now][i][j][x])
        (re+=f[now][i][j][x]%Mod)%=Mod;
    return re;
}

int t[maxn],tp;
bool judge(int x)
{
    int tp=0; while(x) t[++tp]=x%10,x/=10;
    x=root;
    for(int i=tp;i>=1;i--) x=tr[x].son[t[i]];
    return v[x];
}

int main()
{
    scanf("%s",str);
    scanf("%s%s",A,B); d=strlen(A);
    build();

    ll ans=cal(B)-cal(A);
    int x=root;
    for(int i=0;i<d;i++) x=tr[x].son[A[i]-'0'];
    if(v[x]) ans++;
    ans=(ans+Mod)%Mod;
    printf("%I64d\n",ans);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值