zoukankan      html  css  js  c++  java
  • [HNOI2008]GT考试

    矩阵乘法加速动态规划

    step1

    首先先阐述一个sb错误:我刚开始以为给的序列是无关答案的,后来发现其实是不行的,因为例如

    n=4 m=3 数列为101和数列为111时是不一样的答案因为对于1111 1101其一个有重复一个无重复构成的答案不同应该只有我这种蒟蒻会这么想吧

    切入正题:

    在这里设(dp[i][j]) 为文本串到i最多能匹配到模式串的j位上(文本串中i-j+1--i为与模式串1--j位相等)上的数总数

    再引入一个数组(g),(g[i][j])表示在模式串前缀为i后加一个数字使得后缀最多匹配前缀数量为j的方案数

    答案即为(sum_{i=0}^{m-1}dp[n][i])

    可以得出状态转移方程(dp[i][j]=sum_{k=0}^{m-1}{dp[i-1][k]+g[k][j]})

    什么意思呢?其实就是在i位枚举0-9的可能,使i-1位上匹配的k个变为j个。

    然后本问题就可以在(O(n*m^2)) 的时间内解决了

    介绍一下(g)数组的求法,显然涉及到后缀前缀匹配,使用kmp即可。

    void kmp()
    {
    	p[1]=0;
    	for(int i=2;i<=m;i++)
    	{
    		int j=p[i-1];
    		while(s[j+1]!=s[i]&&j)j=p[j];
    		if(s[j+1]==s[i])p[i]=j+1;
    	}
    	for(int i=0;i<=m-1;i++)//在i+1位上放j可以使得最多匹配个数
    	for(int j='0';j<='9';j++)
    	{
    		int k=i;
    		while(s[k+1]!=j&&k)k=p[k];
    		if(s[k+1]==j)k++;
    		g[i][k]++;
    	}
    }
    

    step2

    显然上文的复杂度是不足以通过的。

    此时观察状态转移方程(dp[i][j]=sum_{k=0}^{m-1}{dp[i-1][k]+g[k][j]})

    是不是很像矩阵乘法?

    我们可以建立两个矩阵(DP)矩阵和(G) 矩阵

    [egin{matrix} 1 \ 0 \ 0 \ cdots \ 0 end{matrix} ag{DP} ]

    [egin{matrix} g[0][0] &g[0][1] &cdots &g[0][m-1] \ g[1][0] &cdots & cdots &g[1][m-1] \ cdots &cdots &cdots &cdots \ g[m-1][0] &g[m-1][1] &cdots & g[m-1][m-1] end{matrix} ag{G} ]

    此时发现(DP)矩阵表示(i=0)时的情况即(dp[0][0]=1)其余为0

    (DP)矩阵与(G)矩阵的n次相乘即得出最终答案了

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,mo,tans;
    char s[25];
    int p[25],g[25][25];
    void kmp()
    {
    	p[1]=0;
    	for(int i=2;i<=m;i++)
    	{
    		int j=p[i-1];
    		while(s[j+1]!=s[i]&&j)j=p[j];
    		if(s[j+1]==s[i])p[i]=j+1;
    	}
    	for(int i=0;i<=m-1;i++)
    	for(int j='0';j<='9';j++)
    	{
    		int k=i;
    		while(s[k+1]!=j&&k)k=p[k];
    		if(s[k+1]==j)k++;
    		g[i][k]++;
    	}
    }
    struct node{
    	int p[25][25];
    	node(){memset(p,0,sizeof(p));}
    }ans,G,dp;
    void tim(node &a,node b)
    {
    	node c;
    	for(int k=0;k<=m-1;k++)
    	for(int i=0;i<=m-1;i++)
    	for(int j=0;j<=m-1;j++)
    	c.p[i][j]=(c.p[i][j]+a.p[i][k]*b.p[k][j])%mo;
    	a=c;
    }
    void pow(node &G)
    {
    	int t=n;for(int i=0;i<=m-1;i++)ans.p[i][i]=1;
    	while(t!=1)
    	{
    		if(t%2)tim(ans,G);
    		t/=2;tim(G,G);
    	}
    	tim(ans,G);
    	G=ans;
    }
    int main()
    {
    	scanf("%d%d%d",&n,&m,&mo);
    	cin>>s+1;kmp();dp.p[0][0]=1;
    	for(int i=0;i<=m-1;i++)
    	for(int j=0;j<=m-1;j++)
    	G.p[i][j]=g[i][j];
    	pow(G);tim(dp,G);
    	for(int i=0;i<=m-1;i++)tans=(tans+dp.p[0][i])%mo;
    	printf("%d",tans);
    	return 0;
    }
    
  • 相关阅读:
    处理器及其调度
    java面向对象
    操作系统概述
    mysql 基础操作
    java集合类详解
    java数组
    java方法
    Python—进程间通信
    Python—TCP的黏包问题以及UDP的分片问题
    Python—网络通信编程之tcp非阻塞通信(socketserver)
  • 原文地址:https://www.cnblogs.com/DavidJing/p/10434359.html
Copyright © 2011-2022 走看看