您的位置:首页 > 其它

[BZOJ1009][HNOI2008]GT考试(KMP+DP+矩阵乘法)

2018-04-09 11:04 489 查看

1009: [HNOI2008]GT考试

Time Limit: 1 Sec  Memory Limit: 162 MB
Submit: 4626  Solved: 2878
[Submit][Status][Discuss]

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位的数。 N<=10^9,M<=20,K<=1000

Output

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

Sample Input

4 3 100
111

Sample Output

81

HINT

Source

[Submit][Status][Discuss]

这题要是多模式串可能还容易看出来一些,discuss里有单串AC自动机做法,但是“单串AC自动机”不就是KMP吗。

首先看数据范围猜是矩乘,然后有了显然的DP方程,就可以根据转移递推式上矩乘优化了。

https://www.geek-share.com/detail/2667898764.html

递推式里的p(k,j)需要用KMP处理,仅当k<=j+1时值非零。

注意矩阵只要从0~m-1,因为匹配m位的情况要全部去除。

#include<cstdio>
#include<algorithm>
#include<cstring>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
typedef long long ll;
using namespace std;

const int N=30;
char s
;
int n,m,p,k,ans,nxt
;
struct M{ int a

; M(){ memset(a,0,sizeof(a)); } }f,res;

void getnxt(){
nxt[0]=nxt[1]=0;
rep(i,2,m){
for (k=nxt[i-1]; k && s[k+1]!=s[i]; k=nxt[k]);
if (s[k+1]==s[i]) nxt[i]=k+1; else nxt[i]=0;
}
}

void mul(M &a,M b){
M c;
rep(i,0,m-1) rep(j,0,m-1) rep(k,0,m-1) c.a[i][k]=(c.a[i][k]+a.a[i][j]*b.a[j][k])%p;
rep(i,0,m-1) rep(j,0,m-1) a.a[i][j]=c.a[i][j];
}

M ksm(M a,int b){
M c;
rep(i,0,m-1) c.a[i][i]=1;
for (; b; mul(a,a),b>>=1)
if (b & 1) mul(c,a);
return c;
}

int main(){
freopen("bzoj1009.in","r",stdin);
freopen("bzoj1009.out","w",stdout);
scanf("%d%d%d",&n,&m,&p); scanf("%s",s+1);
getnxt();
rep(i,0,m-1){
for (char j='0'; j<='9'; j++){
for (k=i; k && s[k+1]!=j; k=nxt[k]);
if (s[k+1]==j) f.a[i][k+1]=(f.a[i][k+1]+1)%p; else f.a[i][0]=(f.a[i][0]+1)%p;
}
}
res.a[0][0]=1; mul(res,ksm(f,n));
rep(i,0,m-1) ans=(ans+res.a[0][i])%p;
printf("%d\n",ans);
return 0;
}

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: