• 【XSY3139】预言家 数位DP NFA


    题目描述

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

       结合:(E_1E_2)

       分配:((E_1|E_2|ldots|E_n),ngeq 2)

       重复:((E_1)* ,ngeq 0)

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

      (1leq lleq rleq {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
    ",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;
    }
    
  • 相关阅读:
    idea删除module
    使用腾讯云mysql的一下小坑
    docker 从 tomcat 容器连接到 mysql 容器
    数据结构开发(16):选择排序和插入排序
    数据结构开发(15):递归的思想与应用
    数据结构开发(14):KMP 子串查找算法
    数据结构开发(13):字符串类的创建
    数据结构开发(11):双向循环链表的实现
    数据结构开发(10):Linux内核链表
    数据结构开发(9):循环链表与双向链表
  • 原文地址:https://www.cnblogs.com/ywwyww/p/9267567.html
Copyright © 2020-2023  润新知