zoukankan      html  css  js  c++  java
  • Luogu P3193 GT考试

    题目大意

    给出一个长度为 (m) 的数字串 (A) , 问有多少个长度为 (n) 的数字串 (X) ,满足 (A) 不为 (X) 的子串,请对答案 (mod M)
    (1 leq n leq 10^9)(1 leq m leq 20)(1 leq M leq 1000)(0 leq A_i,X_i leq 9)

    题解

    (f(i,k)) 表示 (X)(i) 位中最后 (k) 位匹配到 (A) 的前 (k) 位,即 (X_{i} = A_{k},X_{i - 1} = A_{k- 1},dots,X_{i - k+1} = A_1) ,且前 (i) 位没有某一段子串与 (A) 匹配成功时, (X) 的前 (i) 位的方案数。
    (g(k,j)) 表示匹配到 (A) 的前 (k) 位,加入一个字符后,变为匹配到 (A) 的前 (j) 位的方案数。
    容易得到

    [f(i,j) = sumlimits_{k = 0}^{m - 1}f(i-1,k) imes g(k,j) ]

    (g(k,j)) 可以用 KMP 预处理,时间复杂度为 (O(m^2))
    然后直接进行上面的 dp ,时间复杂度是 (O(nm^2)) ,显然过不了。
    则答案为 (sumlimits_{i=0}^{m-1} f(n,i))
    观察状态转移方程,发现长得很像矩阵的乘法运算。
    所以我们可以把 (f)(g) 看做矩阵 (F)(G) ,即 (F_i = F_{i-1}G)
    所以 (F_n = F_0 G^n)
    写一个矩阵快速幂就可以把时间复杂度降为 (O(m^2 log n))

    #include <cstdio>
    #include <cstring>
    
    #define MAX_M (20 + 5)
    
    int n, m, mod;
    
    struct Matrix
    {
    	int r, c;
    	int mat[MAX_M][MAX_M];
    	
    	Matrix()
    	{
    		r = 0;
    		c = 0;
    		memset(mat, 0, sizeof mat);
    	}
    	
    	Matrix operator * (Matrix x)
    	{
    		Matrix res;
    		res.r = r;
    		res.c = x.c;
    		for (int i = 0; i < r; ++i)
    		{
    			for (int j = 0; j < x.c; ++j)
    			{
    				for (int k = 0; k < c; ++k)
    				{
    					res.mat[i][j] += mat[i][k] * x.mat[k][j];
    				}
    				res.mat[i][j] %= mod;
    			}
    		}
    		return res;
    	}
    };
    
    Matrix Pow(Matrix x, int y)
    {
    	Matrix res;
    	res.r = res.c = m;
    	for (int i = 0; i < m; ++i)
    	{
    		res.mat[i][i] = 1;
    	}
    	while (y)
    	{
    		if (y & 1) res = res * x;
    		x = x * x;
    		y >>= 1; 
    	}
    	return res;
    }
    
    char s[MAX_M];
    int nxt[MAX_M];
    Matrix f, g;
    int ans;
    
    int main()
    {
    	scanf("%d%d%d%s", &n, &m, &mod, s + 1);
    	f.r = 1;
    	f.c = g.r = g.c = m;
    	for (int i = 1, j = 0; i <= m; ++i)
    	{
    		while (j && s[i + 1] != s[j + 1]) j = nxt[j];
    		if (s[i + 1] == s[j + 1]) ++j;
    		nxt[i + 1] = j;
    	}
    	for (int i = 0; i < m; ++i)
    	{
    		for (char ch = '0'; ch <= '9'; ++ch)
    		{
    			for (int j = i; ; j = nxt[j])
    			{
    				if (s[j + 1] == ch)
    				{
    					++g.mat[i][j + 1];
    					break;
    				}
    				if (!j)
    				{
    					++g.mat[i][j];
    					break;
    				}
    			}
    		}
    	}
    	f.mat[0][0]= 1;
    	f = f * Pow(g, n); 
    	for (int i = 0; i < m; ++i)
    	{
    		ans += f.mat[0][i];
    	}
    	ans %= mod;
    	printf("%d", ans);
    	return 0;
    }
    
  • 相关阅读:
    Android笔记之开机自启
    Android笔记之广播
    Hive笔记之collect_list/collect_set(列转行)
    Hive笔记之数据库操作
    hive笔记之row_number、rank、dense_rank
    Linux Shell管道调用用户定义函数(使shell支持map函数式特性)
    Linux shell爬虫实现树洞网鼓励师(自动回复Robot)
    分享一些免费的接码平台(国外号码)
    爬虫技能之内容提取:如何从有不可见元素混淆的页面中抽取数据
    ctf writeup之程序员密码
  • 原文地址:https://www.cnblogs.com/kcn999/p/13466000.html
Copyright © 2011-2022 走看看