P3193 GT考试

这题题面有毒,复制下来是乱码,这不能怪我,对不对?Mr.Jia?【滑稽】
题目请戳这→ 题面

【解题思路】:

首先我自学了一下矩阵乘法,这题是矩阵乘法与KMP的综合题,很有难度,花了我好长时间调。。。

考虑f[ i ][ j ] = f[ i ][ k ] * g[ k ][ j ],f数组表示选了i个数,末尾字符串匹配字符串a前缀k位,g数组表示从匹配k位到匹配j位的方法种数。答案就是f[n][k] (0<=k<m)根据f数组的定义,我们很容易想到用kmp预处理出next数组。考虑处理g数组,假设当前已经匹配了i位,我们枚举第i+1位选了哪个数字,处理出在选了这个数字的情况下能与前缀匹配几位。

【AC代码】:

#include<bits/stdc++.h>
#define M(a,b) memset(a,b,sizeof(a))
#define pi 3.1415926
using namespace std;
typedef long long ll;
const ll N=25;
ll n,m,mod,ans,next[N],f[N][N],b[N][N];//这题数据有点大,最好开个long long
char s[N];

inline void read(long long &x){//变量使用long long,快读也要记得改成long long
    char ch=getchar(),c=ch;
	x=0;
    while(ch<'0' || ch>'9'){
    	 c=ch;
		 ch=getchar();
	}
    while(ch>='0' && ch<='9'){
    	x=(x<<1)+(x<<3)+ch-'0';
		ch=getchar();
	}
    if(c=='-')x=-x;
}

inline void mul(ll a[25][25],ll b[25][25]){
    ll c[25][25];
    memset(c,0,sizeof(c));
    for(ll i=0;i<m;i++){
        for(ll j=0;j<m;j++){
            for(ll k=0;k<m;k++){
                c[i][j]=(c[i][j]+a[i][k]*b[k][j])%mod;
            }
        }
    }
    memcpy(a,c,sizeof(c));
}

int main(){
    read(n),read(m),read(mod);
    scanf("%s",s+1);
    next[1]=0;
    for(ll i=2,j=0;i<=m;i++){
        while(j&&s[j+1]!=s[i]) j=next[j];
        if(s[j+1]==s[i]) j++;
        next[i]=j; 
    }
    for(ll i=0;i<m;i++){
        for(ll j='0';j<='9';j++){
            ll p=i;
            while(p&&s[p+1]!=j)p=next[p];
            if(s[p+1]==j) p++;
            if(p!=m)b[i][p]=(b[i][p]+1)%mod;
        }
    }
    for(ll i=0;i<m;i++)f[i][i]=1;
    for(ll i=n;i;i>>=1,mul(b,b))if(i&1)mul(f,b);
    for(ll i=0;i<m;i++)ans=(ans+f[0][i])%mod;
    printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值