• [SDOI2014]数数


    [SDOI2014]数数

    这题是AC自动机与各种dp相结合的范例。

    首先,按照套路,我们建出自动AC机。然后,因为是(le N)的所有数的总数,很容易想到数位dp(实际上它如果是在(L,R)之间所有数的总数则更明显)。

    (f_{x,len,lim})表示:

    在自动机上(x)号节点,

    匹配到了母串的(len)位置,

    当前贴上限的状态(lim)(true)为贴着)

    我们很轻易就能得出转移的记忆化:

    int dfs(int x,int len,bool lim){
    	if(!t[x].ok)return 0;
    	if(len==S)return 1;
    	if(f[x][len][lim]!=-1)return f[x][len][lim];
    	int res=0;
    	for(int i=0;i<=(lim?s[len]-'0':9);i++)res=(res+dfs(t[x].ch[i],len+1,lim&&(i==s[len]-'0')))%mod;
    	return f[x][len][lim]=res;
    }
    

    但是,兴冲冲地交上去后,你就会发现:(color{Green}_{80})

    (QaQ?)

    考虑一组数据:

    10
    1
    01
    

    如果你的程序跑出来是(9),恭喜你,上钩了!

    (01)并不是(1)的字串。

    因此我们还要再定义一维前导零状态(lead)

    状态:(f_{x,len,lim,lead})

    在自动机上(x)号节点,

    匹配到了母串的(len)位置,

    当前贴上限的状态(lim)(true)为贴着)

    前导零状态((true)为有)

    则新记忆化状态:

    int dfs(int x,int len,bool lim,bool lead){
    	if(!t[x].ok)return 0;
    	if(len==S)return 1;
    	if(f[x][len][lim][lead]!=-1)return f[x][len][lim][lead];
    	int res=0;
    	for(int i=0;i<=(lim?s[len]-'0':9);i++){
    		if(lead)res=(res+dfs(t[1].ch[i],len+1,lim&&(i==s[len]-'0'),lead&!i))%mod;
    		else res=(res+dfs(t[x].ch[i],len+1,lim&&(i==s[len]-'0'),0))%mod;
    	}
    	return f[x][len][lim][lead]=res;
    }
    

    可以看到,如果有前导零,则直接暴力从根节点开始转移。

    总代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int mod=1e9+7;
    int n,m,S,cnt=1,f[1510][1210][2][2];
    char s[1210],ss[1510];
    struct AC_Automaton{
    	int ch[10],fail;
    	bool ok;
    }t[1510];
    void ins(){
    	int x=1;
    	for(int i=0;i<S;i++){
    		if(!t[x].ch[ss[i]-'0'])t[x].ch[ss[i]-'0']=++cnt,t[cnt].ok=true;
    		x=t[x].ch[ss[i]-'0'];
    	}
    	t[x].ok=false;
    }
    queue<int>q;
    void build(){
    	for(int i=0;i<10;i++){
    		if(t[1].ch[i])t[t[1].ch[i]].fail=1,q.push(t[1].ch[i]);
    		else t[1].ch[i]=1;
    	}
    	while(!q.empty()){
    		int x=q.front();q.pop();
    		for(int i=0;i<10;i++){
    			if(t[x].ch[i])t[t[x].ch[i]].fail=t[t[x].fail].ch[i],q.push(t[x].ch[i]);
    			else t[x].ch[i]=t[t[x].fail].ch[i];
    		}
    		t[x].ok&=t[t[x].fail].ok;
    	}
    }
    int dfs(int x,int len,bool lim,bool lead){
    	if(!t[x].ok)return 0;
    	if(len==S)return 1;
    	if(f[x][len][lim][lead]!=-1)return f[x][len][lim][lead];
    	int res=0;
    	for(int i=0;i<=(lim?s[len]-'0':9);i++){
    		if(lead)res=(res+dfs(t[1].ch[i],len+1,lim&&(i==s[len]-'0'),lead&!i))%mod;
    		else res=(res+dfs(t[x].ch[i],len+1,lim&&(i==s[len]-'0'),0))%mod;
    	}
    	return f[x][len][lim][lead]=res;
    }
    int main(){
    	scanf("%s",s),n=strlen(s),t[1].ok=true,memset(f,-1,sizeof(f));
    	scanf("%d",&m);
    	for(int i=0;i<m;i++)scanf("%s",ss),S=strlen(ss),ins();
    	build();
    	S=n;
    	printf("%d
    ",(dfs(1,0,1,1)-1+mod)%mod);
    	return 0;
    }
    
  • 相关阅读:
    HTML5中的FileSystem API的一个问题(或者是BUG)
    TSQL入门(二)——创建表
    javascript绘制谢尔宾斯基三角形(Sierpinski triangle)
    VS2010操作SQL SERVER CE 4.0数据库
    TSQL入门(三)——增删改(INSERT、DELETE、UPDATE)
    IE6警告框
    TSQL入门(二)——创建表
    TSQL入门(一)——创建数据库
    TSQL入门(一)——创建数据库
    javascript绘制谢尔宾斯基三角形(Sierpinski triangle)
  • 原文地址:https://www.cnblogs.com/Troverld/p/12781189.html
Copyright © 2020-2023  润新知