HDU 3658 How many words (矩阵快速幂&递推)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wust_zzwh/article/details/51834248

In order to make a new word, we will pick out m letters from all the upper case letters and lower case letters(from `a' to `Z'). Therefore, that means you can pick some same letters. But here are two rules:
● as to all the neighbour letters, the absolute value of their ASCII code must be not greater than 32.
● there must be at least one pair of neighbour letters whose absolute value of ASCII code is exactly equal to 32. For example, considering the word in the form like "Xx" or "xX", the neighbour letters have an absolute value of ASCII code exactly equal to 32.
Now how many di erent words can we get?
 

Input
The first line of input is the number of test case. For each test case, there is only one line contains one integer m(2 ≤ m ≤ 10 9).
 

Output
For each test case output one line, just the answer mod 1000000007.
 

Sample Input
 
  
4 2 3 100 7926778
 

Sample Output
 
  
52 4056 533550434 773908369

题意:有52个字母,a-z,A-Z,任取m个(可以重复)构成字符串,问能构成多少种?字符串要满足两个条件:1.任意相邻两个字符ASC码的差的绝对值小于等于32。2.至少有两个相邻的差的绝对值等于32;

题解:就是用52个字母构成长度为m的串。条件二等于32不好搞,先算<=32的所有方案数,在减去<=31的方案数,两者都可以用矩阵快速幂,区别是转移矩阵不同。

递推:令表示以字母A结尾的长度为m的串的种数.52中字母都对应一个F函数,然后就好递推了。

先推<=32

简洁表示为,后面就用这种表示法。


......




.......

第二种:<=31;


建立的矩阵:

#pragma comment(linker, "/STACK:10240000,10240000")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<set>
#include<vector>
#include<map>
#include<stack>
#include<cmath>
#include<algorithm>
using namespace std;
const double R=0.5772156649015328606065120900;
const int N=55;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const double eps=1e-8;
const double pi=acos(-1.0);
typedef long long ll;
ll ini1[N][N],ini2[N][N];
void init()//打表
{
    memset(ini1,0,sizeof ini1);//第一种情况的
    memset(ini2,0,sizeof ini2);//第二种情况的
    int k=26;
    for(int i=0;i<26;i++)
    {
        for(int j=0;j<=k;j++)
        {
            ini1[i][j]=1;
            if(j!=k) ini2[i][j]=1;
        }
        k++;
    }
    k=0;
    for(int i=26;i<52;i++)
    {
        for(int j=k;j<52;j++)
        {
            ini1[i][j]=1;
            if(j!=k) ini2[i][j]=1;
        }
        k++;
    }
}
void cpy(ll a[N][N],ll b[N][N],int n)//矩阵copy,a=b;
{
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
        a[i][j]=b[i][j];
}
ll tmp[N][N];
void mul(ll a[N][N],ll b[N][N])//矩阵乘法并复制,a=a+b;
{
    int n=52;
    memset(tmp,0,sizeof tmp);
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
        for(int k=0;k<n;k++)
        tmp[i][j]=(tmp[i][j]+a[i][k]*b[k][j])%mod;
    cpy(a,tmp,n);
}
ll res[N][N],ini[N][N];
void fast(ll n)//矩阵快速幂
{
    memset(res,0,sizeof res);
    for(int i=0;i<52;i++)
        res[i][i]=1;
    while(n)
    {
        if(n&1)
            mul(res,ini);
        mul(ini,ini);
        n>>=1;
    }
}

ll solve(ll a[N][N],ll n)
{
    cpy(ini,a,52);//为了不破坏初始的转移矩阵,换一个数组搞
    fast(n-1);
    ll ans=0;
    for(int i=0;i<52;i++)
        for(int j=0;j<52;j++)
        ans=(ans+res[i][j])%mod;//乘以最后的初始矩阵
    return ans;
}
int main()
{
    int T_T;
    init();//两个转移矩阵式确定的,先打出来
    cin>>T_T;
    while(T_T--)
    {
        ll m;
        scanf("%I64d",&m);
        ll ans=solve(ini1,m)-solve(ini2,m);//两种情况相减
        ans=(ans%mod+mod)%mod;//
        printf("%I64d\n",ans);
    }
    return 0;
}





猜你喜欢

转载自blog.csdn.net/wust_zzwh/article/details/51834248