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;
}





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