SDOI2014 数数

数数

我们称一个正整数N是幸运数,当且仅当它的十进制表示中不包含数字串集合S中任意一个元素作为其子串。例如当S=(22,333,0233)时,233是幸运数,2333、20233、3223不是幸运数。

给定N和S,计算不大于N的幸运数个数。

租酥雨的题解

AC自动机做数位DP。首先位数小于\(n\)的位数的数只要满足没有不合法串即可,记\(f[i][j]\)表示填了\(i\)个数,当前在AC自动机上编号为\(j\)的节点上的方案数,取答案\(\sum^{n−1}_{i=1}\sum^{tot}_{j=0}f[i][j]\)。注意转移的时候是只能转移到自己Trie图上的儿子而不是儿子通过fail指针串起来的所有点。

位数等于\(n\)的,在前面那个状态上多加一维,表示是否已经严格小于那个数,然后按照数位DP的一般思路卡一卡就好了。

第二次DP的状态是\(f[0/1][i][j]\),0/1是数位dp的那个是否之前所有位都和N相同。

时间复杂度\(O(10 * l * L)\),是18000000。

co int mod=1e9+7,N=1501;

int add(int x,int y)
{
	x+=y;
	return x>mod?x-mod:x;
}

int n;
char s[N],buf[N];
namespace AC
{
	int tot;
	int ch[N][10],val[N],fail[N];
	
	void ins(char s[],int n)
	{
		int u=0;
		for(int i=0;i<n;++i)
		{
			int k=s[i]-'0';
			if(!ch[u][k])
				ch[u][k]=++tot;
			u=ch[u][k];
		}
		val[u]=1;
	}
	
	void getfail()
	{
		std::queue<int>Q;
		for(int i=0;i<10;++i)
			if(ch[0][i])
				Q.push(ch[0][i]);
		while(Q.size())
		{
			int u=Q.front();Q.pop();
			val[u]|=val[fail[u]];
			for(int i=0;i<10;++i)
			{
				if(ch[u][i])
				{
					fail[ch[u][i]]=ch[fail[u]][i];
					Q.push(ch[u][i]);
				}
				else
					ch[u][i]=ch[fail[u]][i];
			}
		}
	}
	
	int dp[2][N][N];
	
	void solve()
	{
		int ans=0;
		dp[0][0][0]=1;
		for(int i=0;i<n;++i)
			for(int j=0;j<=tot;++j)if(!val[j])
				for(int k=0;k<10;++k)if(i+k&&!val[ch[j][k]])
					dp[0][i+1][ch[j][k]]=add(dp[0][i+1][ch[j][k]],dp[0][i][j]);
		for(int i=1;i<n;++i)
			for(int j=0;j<=tot;++j)
				ans=add(ans,dp[0][i][j]);
		memset(dp,0,sizeof dp);
		dp[1][0][0]=1;
		for(int i=0;i<n;++i)
			for(int j=0;j<=tot;++j)if(!val[j])
				for(int k=0;k<10;++k)if(i+k&&!val[ch[j][k]])
				{
					dp[0][i+1][ch[j][k]]=add(dp[0][i+1][ch[j][k]],dp[0][i][j]);
					if(k==s[i+1]-'0')
						dp[1][i+1][ch[j][k]]=add(dp[1][i+1][ch[j][k]],dp[1][i][j]);
					else if(k<s[i+1]-'0')
						dp[0][i+1][ch[j][k]]=add(dp[0][i+1][ch[j][k]],dp[1][i][j]);
				}
		for(int j=0;j<=tot;++j)
			ans=add(ans,add(dp[0][n][j],dp[1][n][j]));
		printf("%d\n",ans);
	}
}

int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	scanf("%s",s+1);
	n=strlen(s+1);
	int m;
	read(m);
	for(int i=1;i<=m;++i)
	{
		scanf("%s",buf);
		AC::ins(buf,strlen(buf));
	}
	AC::getfail();
	AC::solve();
	return 0;
}

posted on 2019-01-26 14:13  autoint  阅读(170)  评论(0编辑  收藏  举报

导航