Brief Description:
一个字符串由n个字符组成,字符只能是大小写的字母(a->z,A->Z),其中a对应0,b对应1.....z对应25,A对应26,B对应27,Z对应51,且字符只能取编号0->m,现在规定某2个字符不能靠在一起,求这样的字符串有多少个。
Analysis:
先不考虑题目的规模,我们定义sum[i][j]:取前i个字符,第i个字符时j,能组成多少个字符串
则有sum[i][j] = sigma(sum[i-1][k]) 且k,j可以靠在一起,时间复杂度为O(26*n).
由于n <= 1e+15,这么做会超时 ,所以只能另辟蹊径!
怎么做呢?线性操作可以考虑矩阵快速幂~正解就是矩阵快速幂。
我们知道sum[1][j] = 1。 0<=j<m
定义矩阵M = (num[i][j]),若i,j可以靠在一起,num[i][j]=1,否则num[i][j]=0
我们用矩阵ansM=(sum[1][0],sum[1][1]....sum[1][m-1])*M^n-1,ansM的第i列数总和就是sum[n][i],
最后我们只需将ansM中的所有数加起来便是答案。
源代码如下:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define M 1000000007
using namespace std;
typedef long long LL;
struct Matrix
{
LL num[64][64];
};
LL n;
int m,K;
LL G[64][64];
Matrix mul(Matrix p,Matrix q) {
Matrix ret;
for(int i=0; i<m; i++) for(int j=0; j<m; j++) {
ret.num[i][j] = 0;
for(int k=0; k<m; k++) {
ret.num[i][j] += p.num[i][k] * q.num[k][j];
ret.num[i][j] %= M;
}
}
return ret;
}
Matrix f()
{
Matrix ret,p;
memset(ret.num,0,sizeof(ret.num));
for(int i=0; i<m; i++) ret.num[i][i] = 1;
for(int i=0; i<m; i++) for(int j=0; j<m; j++) p.num[i][j] = G[i][j];
n--;
while(n)
{
if(n & 1)
ret = mul(ret,p);
p = mul(p,p);
n >>= 1;
}
return ret;
}
int main()
{
while(scanf("%I64d%d%d",&n,&m,&K) == 3)
{
int i,j;
for(i=0; i<m; i++) for(j=0; j<m; j++) G[i][j] = 1;
while(K--)
{
char c1,c2;
int x,y;
scanf(" %c %c",&c1,&c2);
x = c1>='a'&&c1<='z'?c1-'a':c1-'A'+26;
y = c2>='a'&&c2<='z'?c2-'a':c2-'A'+26;
G[x][y] = 0;
}
Matrix ansM = f();
// for(i=0; i<m; i++)
LL ans = 0;
for(i=0; i<m; i++)
{
for(j=0; j<m; j++)
{
ans += ansM.num[i][j];
ans %= M;
}
}
printf("%I64d\n",ans);
}
return 0;
}