【BZOJ 1009】 [HNOI2008]GT考试

【题目链接】:http://www.lydsy.com/JudgeOnline/problem.php?id=1009

【题意】

【题解】

网上有几个博客写得挺好的.
这里发一下链接;
这一篇有把大概怎么做写下来;
http://www.cnblogs.com/BLADEVIL/p/3483694.html
而下面这一篇提到的细节比较好;也讲得比较清楚些;
http://blog.csdn.net/cjk_cjk/article/details/43038377
大概的思路:
假设你现在扫描到了你的准考证号的第i位,同时不吉利数字匹配到了第j位;
也就是说准考证上:i-j+1..i和不吉利数字的1..j匹配好了;
接下来要确定第i+1位是什么;
这里把第i+1位换成第i位,原来的第i位就变成i-1位;
设f[i][j]表示:准考证号前i位中 后j位与不吉利数的前j位相同时,前i位的方案数
则有
f[i][j]=f[i-1][0]*a[0][j]+f[i-1][1]*a[1][j]+…+f[i-1][m-1]*a[m-1][j]
这里的a[k][j]表示的是前一位如果匹配到了第k位,然后想通过增加一个数字变成i个数字 ,然后匹配到第j位,问在第i位有多少个数字可以选择;
这个a数组可以预处理出来;
我们先考虑这个a数组的求法:
比如不吉利数字为
1231243
这里;
假设我们扫描到了第5个数字2;
那么我们就相当于让第i-1个数字匹配到了第5个数字;
然后问你第i个状态哪些状态能由这第5个数字接受到?
它的下一个状态应该可以接匹配到了第3和第6个数字;
即类似
*****12x
****12312x
可能用上面博客的例子好一点:
比如:还是假设不吉利数为123124,那么
f[i][3]=f[i-1][2]+f[i-1][5],因为 f[i-1][2]末尾的*****12不能是**12312,所以需要f[i-1][5]补充 ;
这里的”不能是**12312“可以用一个判重的东西搞出来;
那么像上面这样的a数组要怎么搞呢?
KMP算法!
具体的只能看程序了;
(你需要理解KMP算法的思路才能搞,不然会看得晕头转向);
然后N这么大;
你肯定得写个矩阵快速幂啦;
主要是因为a数组能够独立出来;
因为系数确定了;
所以能够用矩阵;
以后看到这种线性齐次递推式
都能写个矩阵优化;
(我也不知道线性齐次递推式是什么);
f[0][0]=1,f[0][1..m]=0;
最后把f[0][0..m-1]加起来就是答案了;
因为不能全部匹配到嘛.
下面这一段感觉写得很好吧。

/*
f[i][j]的准确含义:
1.f[i][j]表示的每种方案不仅与其后j位有关,还应保证不含不吉利数 
2.为避免重复,f[i][j]表示的每种方案都不含长度大于j且与不吉利数的前缀相同 的后缀 
 否则就会出现:从1到m标号,不吉利数为123124时,f[i][2]计数的方案包含f[i][5]计数的方案 的情况 
 */


【完整代码】

#include <bits/stdc++.h>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define LL long long
#define rep1(i,a,b) for (int i = a;i <= b;i++)
#define rep2(i,a,b) for (int i = a;i >= b;i--)
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define rei(x) scanf("%d",&x)
#define rel(x) scanf("%lld",&x)

typedef pair<int,int> pii;
typedef pair<LL,LL> pll;

const int dx[9] = {0,1,-1,0,0,-1,-1,1,1};
const int dy[9] = {0,0,0,-1,1,-1,1,-1,1};
const double pi = acos(-1.0);
const int M = 30;

struct juzhen
{
    int s[M][M];
    juzhen()
    {
        memset(s,0,sizeof s);
    }
};

int n,m,mod,f[M],cf[300];
char s[M];
juzhen a,F;

juzhen cheng(juzhen A,juzhen B)
{
    juzhen c;
    rep1(i,0,m)
        rep1(j,0,m)
            rep1(k,0,m)
                c.s[i][j] = (c.s[i][j]+A.s[i][k]*B.s[k][j])%mod;
    return c;
}

juzhen ksm(juzhen A,int n)
{
    if (n==1)
        return A;
    juzhen res = ksm(A,n>>1);
    juzhen t = cheng(res,res);
    if (n&1)
        t = cheng(t,A);
    return t;
}

int main()
{
    //freopen("F:\\rush.txt","r",stdin);
    rei(n),rei(m),rei(mod);
    scanf("%s",s+1);

    f[1] = f[2] = 1;
    rep1(i,2,m-1)
    {
        int j = f[i];
        while (j > 1 && s[i]!=s[j]) j = f[j];
        f[i+1] = (s[i]==s[j])?j+1:1;
    }

    int sum;
    rep1(i,0,m-1)
    {
        int j = i+1;
        sum = a.s[i][j] = 1;
        cf[s[j]] = i+1;
        while (j > 1)
        {
            j = f[j];
            if (cf[s[j]]!=i+1)
            {
                cf[s[j]] = i+1;
                a.s[i][j] = 1;
                sum++;
            }
        }
        a.s[i][0] = 10-sum;
    }

    F.s[0][0] = 1;
    F = cheng(F,ksm(a,n));

    int ans = 0;
    rep1(i,0,m-1)
        ans = (ans + F.s[0][i])%mod;
    printf("%d\n",ans);
    return 0;
}
posted @ 2017-10-04 18:45  AWCXV  阅读(161)  评论(0编辑  收藏  举报