首页 > 其他分享 >P3193 [HNOI2008] GT考试 题解

P3193 [HNOI2008] GT考试 题解

时间:2024-05-05 17:00:10浏览次数:18  
标签:zl ch 匹配 题解 矩阵 GT kmp HNOI2008 hdl

之前学矩阵乘的时候做的题,当时因为不会\(kmp\)搜索一稀里糊涂过去了,现在填个坑。

头图

是\(Logos\)!
image


P3193 [HNOI2008] GT考试

题链:洛谷 题库

题目大意:

求有多少个长度为\(n\)的数字串的子串中不包含给出的长度为\(m\)位的串,范围 \(n <= 1e9\),$ m <= 20$。

思路:

首先考虑DP,令\(zl[i][j]\)为原串匹配到第\(i\)位,短串最多可以匹配到第\(j\)位的方案数。

那么显然答案为:

\[\sum_{i=0}^{m-1}zl[n][i] \]

状态转移方程为:

\[zl[i][j]=\sum_{k=0}^{9}zl[i-1][p] \]

其中的\(p\)不一定是\(0\)或者\(j-1\),因为加入字符\(k\)后,会有三种情况产生:

  1. 与原串中的下一个字符匹配;
  2. 失配,无法与任何字符相匹配;
  3. 重新与原串的另一个前缀匹配。

那么上面的式子就无法支持我们完成之后的操作了,所以我们换一种写法。

令\(dh[k][j]\)为一个匹配了长度为\(k\)的串,有多少种增加数字的方法,使得与原串匹配的长度变成\(j\)。

状态转移方程为:

\[zl[i][j]=\sum_{k=0}^{m-1}zl[i][k] \times dh[k][j] \]

由于我们知道原串,所以整个\(dh\)数组是固定的,我们可以预处理出这个数组。方法是用\(kmp\)求出\(next\)数组后,枚举匹配长度\(k\)和字符\(ch\),暴力计算出能匹配到前缀的长度。

那么,由于这道题是矩阵乘法专题里的\(dh\)数组恒不变,显然能想到用矩阵乘法的相关知识来解决。

因为我们最后只需要第\(n\)行矩阵,所以我们把每一行\(zl[i][j]\)抽象成一行,\(m-1\)列的矩阵\(hdl[i]\),可推导出\(hdl[i]=hdl[i-1]*yns\),那么,\(hdl[n]=hdl[0]*yns^n\)。

矩阵快速幂求出\(yns^n\),再用矩阵乘法使其与\(hdl\)相乘,即可得出最终矩阵,再把答案一加一模就ok啦。

code:

#include<bits/stdc++.h>
#define fo(x,y,z) for(int (x)=(y);(x)<=(z);(x)++)
#define fu(x,y,z) for(int (x)=(y);(x)>=(z);(x)--)
typedef long long ll;
namespace Aventurine
{
	inline int qr()
	{
		char ch=getchar();int x=0,f=1;
		for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
		for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);
		return x*f;
	}
	inline void qw(int x)
	{
		if(!x)
			return;
		qw(x/10);
		putchar(x%10+'0');
	}
	inline void qkg(int x)
	{
		if(x==0)
			putchar('0');
		else
			qw(x);
		putchar(' ');
	}
	inline void qhh(int x)
	{
		if(x==0)
			putchar('0');
		else
			qw(x);
		putchar('\n');
	}
}
#define qr qr()
using namespace std;
using namespace Aventurine;
const int Ratio=0;
const int N=55;
const int maxi=INT_MAX;
int n,len,mod,ans;
int kmp[N];
char s[N];
struct rmm
{
	int a[N][N];
	rmm()
	{//一定要初始化!一定要初始化!一定要初始化! 
		memset(a,0,sizeof a);
	}//在结构体中定义的数组需要初始化! 
}yns,hdl;
rmm operator*(rmm a,rmm b)//矩阵乘
{
	rmm c;
	fo(i,0,len-1)
		fo(j,0,len-1)
			fo(k,0,len-1)
				c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j]%mod+mod)%mod;
	return c;
}
rmm operator^(rmm a,int t)//矩阵快速幂
{
	rmm b;
	fo(i,0,len-1)
		b.a[i][i]=1;
	while(t)
	{
		if(t&1)
			b=b*a;
		a=a*a;
		t>>=1;
	}
	return b;
}
namespace Wisadel
{
	void Wprekmp()//kmp初始化
	{
		int j=0;
		fo(i,2,len)
		{
			while(j&&s[j+1]!=s[i])
				j=kmp[j];
			if(s[j+1]==s[i])
				j++;
			kmp[i]=j;
		}
	}
	void Wwork()
	{
		fo(i,0,len-1)
			for(char ch='0';ch<='9';ch++)
			{//枚举添加的字符 
				int j=i;
				while(j&&s[j+1]!=ch)
					j=kmp[j];
				if(s[j+1]==ch)
					j++;
				yns.a[i][j]=(yns.a[i][j]+1)%mod;
			}
		hdl.a[0][0]=1;//即为hdl[0] 
		yns=yns^n;
		hdl=hdl*yns;
		fo(i,0,len-1)
			ans=(ans+hdl.a[0][i])%mod;
	}
	short main()
	{
		n=qr,len=qr,mod=qr;
		scanf("%s",s+1);
		Wprekmp();
		Wwork();
		printf("%d\n",ans);
		return Ratio;
	}
}
int main(){return Wisadel::main();}

完结撒花

image
我放两张。
image
谁有W的好图啊aa球球了QwQ

标签:zl,ch,匹配,题解,矩阵,GT,kmp,HNOI2008,hdl
From: https://www.cnblogs.com/Ratio-Yinyue1007/p/18173636

相关文章

  • CF1630A And Matching 题解
    题目描述有\(n\)个数\(0,1,2,\cdots,n-1\)。你需要把他们两两分组,使得每组两个数按位与的结果之和\(=k\)。如果可能,请构造出一组可能的\(\fracn2\)个数对,否则输出-1。保证\(n\)是\(2\)的幂,\(k\len-1\)思路首先我们发现,\(n\)是二的幂,所以按照二进制的角度看,这......
  • [SDOI2015] 星际战争 题解
    假如将所有激光武器放在一边,所有机器人放在一边,激光武器向它可以伤害的机器人连边,再加超级源/汇点,这就是一个网络流问题。考虑激光武器向机器人连的边容量无限,而机器人向超级汇点连的边容量为机器人的装甲值,而超级源点连向激光武器的边则是用时\(\times\)激光武器伤害。发现假......
  • CF729B Spotlights 题解
    题目简述给出一个$n$行$m$列的$01$矩阵,定义每个点的价值为上下左右四个方向有$1$的方向数,求所有为$0$的点的价值和。题目分析我们首先可以考虑暴力,但是发现是不行的。于是我们考虑动态规划。设$dp_{i,j,0/1/2/3}$分别表示$(i,j)$这个点上方,左方,下方,右方是否有$......
  • Linux 输出重定向 2>&1 , 1>&2
    在shell程式中,最常使用的FD(filedescriptor)大概有三个,分别是:0是一个文件描述符,表示标准输入(stdin)1 是一个文件描述符,表示标准输出(stdout)2 是一个文件描述符,表示标准错误(stderr)在标准情况下,这些FD分别跟如下设备关联: stdin(0):keyboard键盘输入,并返回......
  • [SCOI2007] 蜥蜴 题解
    发现实际上就是在求有多少只蜥蜴能逃出来。发现可以将柱子拆成入点和出点两部分,自己的出点向别人的入点连边,自己的入点向自己的出点连边。最后再加一个超级源点\(S\),连接所有有蜥蜴的柱子入点;再加一个超级汇点\(T\),连接所有能够跳出地图的柱子。我们猛然发现:这个问题不就是求......
  • 题解:AT_abc352_C [ABC352C] Standing On The Shoulders
    考场憋了很久,最后代码贼短……理想状态下,直接全排列解决问题。但是,\(1\len\le2\times10^5\),明显TLE,试都不用试的。咋优化呢?其实,前面的巨人只负责“打地基”,作为“塔尖儿”的巨人有且仅有1个。而前面地基随便排列,地基高度(他们的和)都不会变。所以,我们只需要枚举塔尖即......
  • 仓储层当前有接口 IRepository<T> 抽象类 BaseRepository<T> 业务逻辑层有抽象类 Bas
    以下是一个简单的C#示例,展示了如何实现不同表对应不同的业务逻辑层和不同的仓储实例://仓储层publicinterfaceIRepository<T>{voidAdd(Tentity);voidUpdate(Tentity);voidDelete(Tentity);TGetById(intid);//其他仓储操作方法...}publ......
  • 牛客小白月赛92 题解
    牛客小白月赛92题解A.获得木头签到\((x\times4)/2\times4=x\times8\)#include<bits/stdc++.h>usingnamespacestd;#definefffirst#definesssecond#definepbpush_back#defineall(u)u.begin(),u.end()#defineendl'\n'#definedebug(x......
  • 牛客 215E 黄魔法师 题解
    Description给出\(n,k\),求一个长度为\(n\)的数组\(a\),满足有恰好\(k\)对数对\((i,j)(1\leqi<j\leqn)\)满足\(a_i+a_j\)为完全平方数。如果不存在,输出\(-1\)。linkSolution显然如果\(k>\binom{n}{2}\)就一定无解。构造时会发现肯定要尽量弄成相同的......
  • 题解【[ABC155F] Perils in Parallel】(未完成)
    题目链接两个常规转化:灯的坐标与区间坐标都很大,不妨将其离散化,转化为\(1\simn\)的点与\(1\simn\)的操作区间。对于一段区间取反,可以理解为对一段区间异或\(1\),转化为在异或差分数组上操作,即差分数组\(diff_i=a_i\bigoplusa_{i-1}\),区间\([l,r]\)异或\(1\)转化为差......