【XSY3139】预言家 数位DP NFA

题目描述

  有一个定义在 \(\{0,1,2,3,4,5,6,7,8,9\}\) 上的合规表达式,包含三种基本的操作:

   结合:\(E_1E_2\)

   分配:\((E_1|E_2|\ldots|E_n),n\geq 2\)

   重复:\((E_1)* ,n\geq 0\)

  给你 \(l,r\),问你有多少个 \([l,r]\) 之间不含前导零的整数能匹配这个合规表达式。

  \(1\leq l\leq r\leq {10}^{18}\)

题解

  直接建出这个合规表达式对应的 NFA,在上面跑数位 DP 即可。

  记录 \(f_{i,0/1,j}\) 表示还需要确定后 \(i\) 位,前面这几位是否比 \(n\) 小,在 NFA 上面可以达到的状态集合是 \(j\) 时的方案数。

  时间复杂度:\(O(???)\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;

char s[100];
int c[100];//另一个括号的位置
int n;
int st[100];
int top;
int b[100][100];//epsilon
int nxt[100];
int move(int x,int v)
{
    int res=0;
    for(int i=1;i<=n+1;i++)
        if(((x>>(i-1))&1)&&s[i]-'0'==v)
            res|=nxt[i+1];
    return res;
}
void init()
{
    memset(c,0,sizeof c);
    top=0;
    for(int i=1;i<=n;i++)
        if(s[i]=='(')
            st[++top]=i;
        else if(s[i]==')')
        {
            c[i]=st[top];
            c[st[top]]=i;
            top--;
        }
    memset(b,0,sizeof b);
    for(int i=1;i<=n+1;i++)
        b[i][i]=1;
    for(int i=1;i<=n;i++)
        if(s[i]=='(')
        {
            if(s[c[i]+1]=='*')
            {
                b[i][c[i]]=1;
                b[i][i+1]=1;
            }
            else
            {
                b[i][i+1]=1;
                int cnt=0;
                for(int j=i;j<=c[i];j++)
                {
                    if(s[j]=='(')
                        cnt++;
                    else if(s[j]==')')
                        cnt--;
                    if(s[j]=='|'&&cnt==1)
                    {
                        b[i][j+1]=1;
                        b[j][c[i]]=1;
                    }
                }
            }
        }
        else if(s[i]==')')
        {
            b[i][i+1]=1;
            if(s[i+1]=='*')
                b[i][c[i]]=1;
        }
        else if(s[i]=='*')
            b[i][i+1]=1;

    for(int k=1;k<=n+1;k++)
        for(int i=1;i<=n+1;i++)
            for(int j=1;j<=n+1;j++)
                b[i][j]|=b[i][k]&&b[k][j];
    memset(nxt,0,sizeof nxt);
    for(int i=1;i<=n+1;i++)
        for(int j=1;j<=n+1;j++)
            if(b[i][j])
                nxt[i]|=1<<(j-1);
}
int len;
int a[100];
map<int,ll> f[100][2];
ll calc(ll m)
{
    if(!m)
        return 0;
    len=0;
    while(m)
    {
        a[++len]=m%10;
        m/=10;
    }
    for(int i=0;i<=len;i++)
    {
        f[i][0].clear();
        f[i][1].clear();
    }
    for(int i=1;i<=a[len];i++)
        f[len-1][i==a[len]][move(nxt[1],i)]++;
    for(int i=len-1;i>=1;i--)
        for(int j=1;j<=9;j++)
            f[i-1][0][move(nxt[1],j)]++;
    for(int i=len;i>=1;i--)
    {
        for(auto v:f[i][1])
            if(v.first)
            {
                 for(int j=0;j<=a[i];j++)
                     f[i-1][j==a[i]][move(v.first,j)]+=v.second;
            }
        for(auto v:f[i][0])
            if(v.first)
            {
                 for(int j=0;j<=9;j++)
                     f[i-1][0][move(v.first,j)]+=v.second;
            }
    }
    ll res=0;
    for(int i=0;i<=1;i++)
        for(auto v:f[0][i])
            if((v.first>>n)&1)
                res+=v.second;
    return res;
}
void solve()
{
    ll l,r;
    scanf("%lld%lld",&l,&r);
    scanf("%s",s+1);
    n=strlen(s+1);
    init();
    ll ans1=calc(r);
    ll ans2=calc(l-1);
    ll ans=ans1-ans2;
    printf("%lld\n",ans);
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
#endif
    int t;
    scanf("%d",&t);
    while(t--)
        solve();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ywwyww/p/9267567.html