• 【BZOJ】1009: [HNOI2008]GT考试(dp+矩阵乘法+kmp+神题)


    http://www.lydsy.com/JudgeOnline/problem.php?id=1009

    好神的题orzzzzzzzzzz

    首先我是连递推方程都想不出的人。。。一直想用组合来搞。。看来我是sb。。

    设f[i,j]表示前i个字符匹配了前j个不吉利数字的方案,即i-j+1~i都是不吉利数字

    那么答案就是sigma{f[n,i], 0<=i<m}

    转移是

    f[i+1,k]=sum{f[i, j],枚举i+1的字符后,k是i+1字符和不吉利数字匹配1~k,0<=k<=j}

    发现k可以由kmp一样的适配数组得到

    而我们发现,每一个阶段i~i+1的转移都是枚举i+1然后找j的失配,也就是说,所有的转移都是一样的。

    方程又是求和,那么可以考虑矩阵乘法优化(orzzzzz

    即根据

    $$c[i,j]=sum a[i,k] imes b[k,j]$$

    则状态f[i+1]和f[i]的矩阵转移可看做

    $$f_{i+1}[1, j]=sum f_{i}[1,k] imes A[k, j]$$

    所以我们可以逆推出矩阵$A$,即它表示的意思是从k转移到j上的倍数

    所以我们可以kmp一次不吉利数字,求出$A$,然后就可以矩乘logn求出$A^n$做出本题

    最后的答案是求出的$A^n$后,乘上$f_{1}$得到$f_n$然后累计$f_n[1, i], 0<=i<m$

    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <string>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    #include <set>
    #include <map>
    using namespace std;
    typedef long long ll;
    #define pii pair<int, int>
    #define mkpii make_pair<int, int>
    #define pdi pair<double, int>
    #define mkpdi make_pair<double, int>
    #define pli pair<ll, int>
    #define mkpli make_pair<ll, int>
    #define rep(i, n) for(int i=0; i<(n); ++i)
    #define for1(i,a,n) for(int i=(a);i<=(n);++i)
    #define for2(i,a,n) for(int i=(a);i<(n);++i)
    #define for3(i,a,n) for(int i=(a);i>=(n);--i)
    #define for4(i,a,n) for(int i=(a);i>(n);--i)
    #define CC(i,a) memset(i,a,sizeof(i))
    #define read(a) a=getint()
    #define print(a) printf("%d", a)
    #define dbg(x) cout << (#x) << " = " << (x) << endl
    #define error(x) (!(x)?puts("error"):0)
    #define printarr2(a, b, c) for1(_, 1, b) { for1(__, 1, c) cout << a[_][__]; cout << endl; }
    #define printarr1(a, b) for1(_, 1, b) cout << a[_] << '	'; cout << endl
    inline const int getint() { int r=0, k=1; char c=getchar(); for(; c<'0'||c>'9'; c=getchar()) if(c=='-') k=-1; for(; c>='0'&&c<='9'; c=getchar()) r=r*10+c-'0'; return k*r; }
    inline const int max(const int &a, const int &b) { return a>b?a:b; }
    inline const int min(const int &a, const int &b) { return a<b?a:b; }
    
    const int N=22;
    typedef int mtx[N][N];
    mtx a, t, f, b;
    int n, m, MD, p[N];
    char s[N];
    void mul(mtx a, mtx b, mtx c, int la, int lb, int lc) {
    	rep(i, la) rep(j, lc) {
    		t[i][j]=0;
    		rep(k, lb) t[i][j]=(t[i][j]+(a[i][k]*b[k][j])%MD)%MD;
    	}
    	rep(i, la) rep(j, lc) c[i][j]=t[i][j];
    }
    int main() {
    	read(n); read(m); read(MD);
    	scanf("%s", s+1);
    	int j=0;
    	for(int i=2; i<=m; ++i) {
    		while(j && s[i]!=s[j+1]) j=p[j];
    		if(s[i]==s[j+1]) ++j;
    		p[i]=j;
    	}
    	rep(i, m) for1(k, 0, 9) {
    		j=i;
    		while(j && s[j+1]-'0'!=k) j=p[j];
    		if(s[j+1]-'0'==k) ++j;
    		if(j<m) a[i][j]=(a[i][j]+1)%MD;
    	}
    	rep(i, m) b[i][i]=1;
    	while(n) {
    		if(n&1) mul(b, a, b, m, m, m);
    		mul(a, a, a, m, m, m);
    		n>>=1;
    	}
    	int ans=0;
    	f[0][0]=1;
    	mul(f, b, f, 1, m, m);
    	rep(i, m) ans=(ans+f[0][i])%MD;
    	print(ans);
    	return 0;
    }
    

      


    Description

    阿申准备报名参加GT考试,准考证号为N位数X1X2....Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字。他的不吉利数学A1A2...Am(0<=Ai<=9)有M位,不出现是指X1X2...Xn中没有恰好一段等于A1A2...Am. A1和X1可以为0

    Input

    第一行输入N,M,K.接下来一行输入M位的数。 100%数据N<=10^9,M<=20,K<=1000 40%数据N<=1000 10%数据N<=6

    Output

    阿申想知道不出现不吉利数字的号码有多少种,输出模K取余的结果.

    Sample Input

    4 3 100
    111

    Sample Output

    81

    HINT

     

    Source

  • 相关阅读:
    C#实现京东登录密码加密POST
    查询SQL Server数据库所有表字段备注
    DataGridView数值列和日期列
    (转)Android 系统 root 破解原理分析
    Dynamics AX 中重点数据源方法
    .NET中Debug模式与Release模式
    DotNetBar的初步使用
    省市区联动小功能
    多余的Using Namespaces或引用会影响程序的执行效率么?
    MSIL指令集
  • 原文地址:https://www.cnblogs.com/iwtwiioi/p/4101576.html
Copyright © 2020-2023  润新知