QQ 的排列
Description
QQ向来对排列问题很感兴趣。什么全排列呀,置换群呀,都是QQ曾经研究过的问题。现在,一般难度的排列问题已经难不倒QQ了,QQ想要更高程度的挑战。于是QQ经常找全全和样样切磋很多高难度的题目。全全和样样都是研究排列的百年难得一遇的奇才,对此也是乐此不疲哦。
最近,他们发现他们三个人的昵称居然都是由重复的字组成的。这个可是一个很有趣的现象呀。于是,QQ的奇思妙想又出来了,他们每个人的昵称的字都提取出来,这样就获得了Q,全,样这三个字,他希望用这三个字重新组成新的昵称,每个字都可以用无限次,当然这样的昵称很可能很难听。 QQ又在这些排列上加了一个附加的条件,就是Q这个字母不能连续出现两次。
比如说,我们令Hn表示当昵称由n个字组成时,问有多少个符合条件的昵称。
例如:
H1=3,具体方案:Q,样,全
H2=8,具体方案:Q样,Q全,样Q,样全,样样,全Q,全样,全全
再令Sn=H1+H2+…+Hn-1+Hn;
QQ的问题出来了,他想知道Sn,但是这个Sn有可能太大了,所以QQ只想知道这个Sn对9937取余的结果。
Input
先输入一个数T,表示有多少组测试数据
随后的T行,每行一个数n。
n的范围是1到1000000000;
Output
对于每个数n,计算Sn mod 9937。
Sample Input
2 1 2
Sample Output
3 11
HINT
Source
这道题是典型的组合数学题,假设长度为n(正好长度为n,不包括长度小于n的)的方法g(n),sum(n)为长度不大于n的总和;
sum(n) = g(1)+ g(2)+ …… + g(n);
那么我们可以分析如下 g(n) = 2*g(n-1)+2*g(n-2);
其中2*g(n-1)是第一个字符为'全'或者'样'的时候的种类,当第一个字符为'Q'的时候,那么第二个字符只能是'全'或者'样',那么这样第一个字符和第二个字符就不会影响到后面的字符,所以第一个字符为'Q'的时候,种类为2*g(n-2)。我们可以得到如下一些式子:
g(3) = 2*g(2) + 2*g(1);
g(4) = 2*g(3) + 2*g(2);
……
g(n)= 2*g(n-1) + 2*g(n-2);
那么将这n-2个式子加起来可以得到sum(n) = 2*sum(n-1)+2*sum(n-2)+g(2)- g(1);
易知g(1) = 3,g(2) = 8;
可以得到如下一个矩阵
0 1 0sum(n-2)
2 2 5 = A sum(n-1) = B
0 0 11
其中A矩阵中的5是等于g(2)- g(1),A矩阵乘以B矩阵可以得到的矩阵C=(sum(n-1), sum(n), 1)
所以我们现在要做的事对于(sum(1), sum(2), 1)做n-2次A矩阵的左乘,所以利用二分求幂的思想,利用log(n)的复杂度既可以将问题求解出来。
#include <cstdio>
void MatrixMul(int fri[][3], int next[][3])
{
int cnt[3][3];
for ( int i = 0; i < 3; ++i )
{
for ( int j = 0; j < 3; ++j )
{
cnt[i][j] = 0;
for ( int k = 0; k < 3; ++k )
{
cnt[i][j] += fri[i][k] * next[k][j];
}
cnt[i][j] = cnt[i][j] % 9937;
}
}
for ( int i = 0; i < 3; ++i )
{
for ( int j = 0; j < 3; ++j )
{
next[i][j] = cnt[i][j];
}
}
}
int getAns(int n)
{
if ( n == 1 )
{
return 3;
}
if ( n == 2 )
{
return 11;
}
int key[3][3] = {{0,1,0},{2,2,5},{0,0,1}}, ans[3][3] = {{0,1,0},{2,2,5},{0,0,1}};
int temp[3][1] = {3, 11, 1};
n -= 3;
while ( n )
{
if ( n&1 )
{
MatrixMul(key, ans);
}
n = n >> 1;
MatrixMul(key, key);
}
int num = 0;
for ( int i = 0; i < 3; ++i )
{
num += ans[1][i] * temp[i][0];
}
return num % 9937;
}
int main(int argc, char ** argv)
{
int t, n;
scanf("%d", &t);
while ( t-- )
{
scanf("%d", &n);
printf("%d\n", getAns(n));
}
return 0;
}