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取余的结果。
先输入一个数T,表示有多少组测试数据
随后的T行,每行一个数n。
n的范围是1到1000000000;
对于每个数n,计算Sn mod 9937。
2 1 2
3 11
浙师大2010校赛
这道题是典型的组合数学题,假设长度为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; }