HDU 5860 cjj's string game

题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5863


题意:用k个不同的字符,形成两个长度为n的字符串中,可以有多少种构建两个字符串的方法,使得所有的连续子串中,对应的两串相同的最大子串长度为过m。


思路:建立转移矩阵,对于两串前i个字符,有两种情况:一种是前面连续相同最大子串长度没有达到过m,可以0~m-1任意一种;第二种是前面已经出现过一个相同子串长度为m的串了,现在结尾处的相同连续子串为0~m种。考虑好每种情况的转移关系,直接用矩阵快速幂计算,答案是转移n次后。把已经出现过一个相同子串长度为m的串的所有情况加和就是答案。


#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <sstream>
#include <queue>
#include <utility>
using namespace std;

#define rep(i,j,k) for (int i=j;i<=k;i++)
#define Rrep(i,j,k) for (int i=j;i>=k;i--)

#define Clean(x,y) memset(x,y,sizeof(x))
#define LL long long
#define ULL unsigned long long
#define inf 0x7fffffff
#define mod 1000000007
const int maxn = 11*2;
int n,m,k;
struct node
{
    LL a[maxn][maxn];
    void f()
    {
        Clean(a,0);
        rep(i,0,m-1)
            a[i][0] = k * (k-1);
        rep(i,1,m-1)
            a[i-1][i] = k;

        rep(i,m,2*m)
            a[i][m] = k*(k-1);

        rep(i,m+1,2*m)
            a[i-1][i] = k;

        a[m-1][2*m] = k;
    }
    void g()
    {
        Clean(a,0);
        rep(i,0,2*m)
            a[i][i] = 1;
    }
};

node multi( node &x , node &y )
{
    node ans;
    Clean(ans.a,0);
    rep(i,0,2*m)
        rep(j,0,2*m)
            rep(k,0,2*m)
            ans.a[i][j] = ( ans.a[i][j] + x.a[i][k] * y.a[k][j] ) % mod;
    return ans;
}

int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        scanf("%d%d%d",&n,&m,&k);
        node x,temp;
        x.g();
        temp.f();

        LL ans = 0;
        while( n )
        {
            if ( n & 1 ) x = multi( temp , x );
            temp = multi( temp , temp );
            n >>= 1;
        }
        rep(i,m,2*m) ans = ( ans + x.a[0][i] ) % mod;
        printf("%I64d\n",ans);
    }
    return 0;
}





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章