zoukankan      html  css  js  c++  java
  • 【bzoj3530】[Sdoi2014]数数 AC自动机+数位dp

    题目描述

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

    输入

    输入的第一行包含整数N。
    接下来一行一个整数M,表示S中元素的数量。
    接下来M行,每行一个数字串,表示S中的一个元素。

    输出

    输出一行一个整数,表示答案模109+7的值。

    样例输入

    20
    3
    2
    3
    14

    样例输出

    14


    题解

    AC自动机+数位dp

    同学的某道考试题的加强版。。。

    由于给定的串多且复杂,要求不能匹配到这些串,所以可以对这些串建立Trie图。

    然后设$f[i][j]$表示从$j$开始走$i$个节点,不触碰到危险节点(即得到的是非幸运数)的方案数。

    那么如果$j$是危险节点则方案数为0,否则枚举$j$走出去的第一步,用$f[i-1][next[j][k]]$更新$f[i][j]$。

    考虑数位dp的过程:

    首先处理出位数不满$n$位的数的个数:枚举位数和最高位(非0),然后方案数即为$f[i][next[1][j]]$。

    然后考虑位数满$n$位的数的个数:

    考虑从高到底的每一位:从0到当前位-1是满的(即后面的数恰好从0到10^{位数}),因此可以直接从$f$中取出。再考虑当前位不满的情况,此时转化为了子问题,按照同样的方法处理即可。

    注意最高位是不能包含前导0的,而确定最高位以后其余的位是可以包含前导0的。

    时间复杂度$O(10nL)$

    细节贼多。。。代码凑合着看吧。。。

    #include <queue>
    #include <cstdio>
    #include <cstring>
    #define N 1510
    #define mod 1000000007
    using namespace std;
    queue<int> q;
    int next[N][10] , tot = 1 , fail[N] , tag[N] , f[N][N];
    char s[N] , w[N];
    void build()
    {
    	int x , i;
    	for(i = 0 ; i < 10 ; i ++ ) next[0][i] = 1;
    	q.push(1);
    	while(!q.empty())
    	{
    		x = q.front() , q.pop() , tag[x] |= tag[fail[x]];
    		for(i = 0 ; i < 10 ; i ++ )
    		{
    			if(next[x][i]) fail[next[x][i]] = next[fail[x]][i] , q.push(next[x][i]);
    			else next[x][i] = next[fail[x]][i];
    		}
    	}
    }
    int main()
    {
    	int n , m , i , j , k , t , flag , ans = 0;
    	scanf("%s%d" , s , &m) , n = strlen(s);
    	for(i = 1 ; i <= m ; i ++ )
    	{
    		scanf("%s" , w);
    		for(j = 0 , t = 1 ; w[j] ; j ++ )
    		{
    			if(!next[t][w[j] ^ '0']) next[t][w[j] ^ '0'] = ++tot;
    			t = next[t][w[j] ^ '0'];
    		}
    		tag[t] = 1;
    	}
    	build();
    	for(i = 1 ; i <= tot ; i ++ ) f[1][i] = !tag[i];
    	for(i = 2 ; i <= n ; i ++ )
    		for(j = 1 ; j <= tot ; j ++ )
    			if(!tag[j])
    				for(k = 0 ; k < 10 ; k ++ )
    					f[i][j] = (f[i][j] + f[i - 1][next[j][k]]) % mod;
    	for(i = 1 ; i < n ; i ++ )
    		for(j = 1 ; j < 10 ; j ++ )
    			ans = (ans + f[i][next[1][j]]) % mod;
    	for(i = 0 , t = flag = 1 ; i < n ; i ++ )
    	{
    		for(j = flag ; j < (s[i] ^ '0') ; j ++ ) ans = (ans + f[n - i][next[t][j]]) % mod;
    		t = next[t][s[i] ^ '0'];
    		if(tag[t]) break;
    		flag = 0;
    	}
    	if(!tag[t]) ans = (ans + 1) % mod;
    	printf("%d
    " , ans);
    	return 0;
    }
    

     

  • 相关阅读:
    CRC16冗余循环检测计算器好用。modbus RTU
    WINCC 应用与提高(78讲15.98G)视频教程网盘下载
    MFC win32 API串口同步模式代码示范
    arduino连接12864LCD方法ST7920
    arduino连接LCD1602LCD方法
    WIN10下如何解决PL2303驱动不可用的问题或者com口显示黄色感叹号usbtoserial
    SQL Server类型与C#类型对应关系
    SQL Server 阻止了对组件 'Ad Hoc Distributed Queries' 的 STATEMENT'OpenRowset/OpenDatasource' 的访问 .
    sql中如何调用另一台服务器的数据库查询数据呢?
    SQL Server 触发器
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7598358.html
Copyright © 2011-2022 走看看