Problem Description
医学界发现的新病毒因其蔓延速度和Internet上传播的"红色病毒"不相上下,被称为"红色病毒",经研究发现,该病毒及其变种的DNA的一条单链中,胞嘧啶,腺嘧啶均是成对出现的。
现在有一长度为N的字符串,满足一下条件:
(1) 字符串仅由A,B,C,D四个字母组成;
(2) A出现偶数次(也可以不出现);
(3) C出现偶数次(也可以不出现);
计算满足条件的字符串个数.
当N=2时,所有满足条件的字符串有如下6个:BB,BD,DB,DD,AA,CC.
由于这个数据肯能非常庞大,你只要给出最后两位数字即可.
现在有一长度为N的字符串,满足一下条件:
(1) 字符串仅由A,B,C,D四个字母组成;
(2) A出现偶数次(也可以不出现);
(3) C出现偶数次(也可以不出现);
计算满足条件的字符串个数.
当N=2时,所有满足条件的字符串有如下6个:BB,BD,DB,DD,AA,CC.
由于这个数据肯能非常庞大,你只要给出最后两位数字即可.
Input
每组输入的第一行是一个整数T,表示测试实例的个数,下面是T行数据,每行一个整数N(1<=N<2^64),当T=0时结束.
Output
对于每个测试实例,输出字符串个数的最后两位,每组输出后跟一个空行.
Sample Input
4 1 4 20 11 3 14 24 6 0
Sample Output
Case 1: 2 Case 2: 72 Case 3: 32 Case 4: 0 Case 1: 56 Case 2: 72 Case 3: 56如果没有任何条件限制,A、B、C、D组成长度为n的字符串,其个数应该为:4^n。
题目要求得到合法序列,而合法序列显然可由不合法序列加A或C得到,所以我们的任务有:
得到求F(n)所需的合法序列F(n-2) 并求出不合法序列F (n - 1) ;
因为有了A、C需要出现偶数次的要求,就出现合法和不合法的不同分组。 在不合法的组里,又有 1.A出现奇数次、C出现偶数次; 2.C出现奇数次、A出现偶数次; 3.A出现奇数次、C出现奇数次; 三种情况。
我们用数组 f[n][0]保存长度为n,合法的字符串的个数。 /*ans*/ f[n][1]保存长度为n,仅A出现奇数次的字符串的个数。 f[n][2]保存长度为n,仅C出现奇数次的字符串的个数。 f[n][3]保存长度为n,A、C出现奇数次的字符串的个数。
f[n][0]
长度为n-1的合法字符串在末尾加上一个B或者D,都可以变成长度为n的合法字符串。
长度为n-1的仅A出现奇数次的字符串再在末尾加上一个A,也可以变成合法字符串。
长度为n-1的仅C出现奇数次的字符串再在末尾加上一个C,也可以变成合法字符串。
所以f[n][0] = 2 × f[n-1][0] + f[n-1][1] + f[n-1][2];同理f[n][1] = 2 × f[n-1][1] + f[n-1][0] + f[n-1][3];f[n][2] = 2 × f[n-1][2] + f[n-1][0] + f[n-1][3];f[n][3] = 2 × f[n-1][3] + f[n-1][1] + f[n-1][2];f[1][0] = 2
f[1][1] = 1
f[1][2] = 1
f[1][3] = 0由对称性易知 : f[n][1] = f[n][2]又有f[n][0] + f[n][3] = f[n][1] + f[n][2]结合f[n][0] + f[n][1] + f[n][2] + f[n][3] = 4^n所求ans为f[n][0]所以我们从中解出f[n][0] = 2 × f[n-1][0] + 2^(2*n - 3)由递推公式得到通项公式:f[n][0] = 2^(2*n - 2) + 2^(n - 1)因为题目只要求输出最后2位数,通项公式中2^n 显然是有循环节的~
2^0 -> 1
2^1 -> 2
2^2 -> 4
2^3 -> 8
2^4 -> 16
2^5 -> 32
2^6 -> 64
2^7 -> 28
2^8 -> 56
2^9 -> 12
2^10 -> 24
2^11 -> 48
2^12 -> 96
2^13 -> 92
2^14 -> 84
2^15 -> 68
2^16 -> 36
2^17 -> 72
2^18 -> 44
2^19 -> 88
2^20 -> 76
2^21 -> 52
2^22 -> 4
到了2^22时,末尾2位又变成4,与22一样,这时候就进入了一个循环了(每20个一次循环)。
所以,结果只能是这22个中的一个。只有n=0 和 n=1是需要特殊考虑的。其他n就等于2(n-2) % 20 + 2的值了。最下面的方法二 用的矩阵快速幂形式, 待分析#include <stdio.h> int main(void) { __int64 n; int c, t; int d[] = { 4, 8,16,32,64,28,56,12,24,48, 96,92,84,68,36,72,44,88,76,52 }; while (scanf("%d", &t), t) { for (c = 1; c <= t; c++) { scanf("%I64d", &n); printf("Case %d: %d\n", c, n<3?(n<2?2:6):(d[(2*n-4)%20]+d[(n-3)%20])%100); } putchar('\n'); } return 0; }
#include <stdio.h> #include <stdlib.h> #include <math.h> #include <string.h> #include <string> #include <vector> #include <map> #include <set> #include <queue> #include <algorithm> #include <iostream> #include <sstream> #define ll __int64 using namespace std; const int mat1_[4][4] = { {1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1} }; const int mat2_[4][4] = { {2, 1, 1, 0}, {1, 2, 0, 1}, {1, 0, 2, 1}, {0, 1, 1, 2} }; int mat1[4][4], mat2[4][4], mat3[4][4]; void mul(int mat1[4][4], int mat2[4][4]) { int i, j, k; for (i = 0; i < 4; i++) { for (j = 0; j < 4; j++) { mat3[i][j] = 0; for (k = 0; k < 4; k++) mat3[i][j] += mat1[i][k] * mat2[k][j]; mat3[i][j] %= 100; } } memcpy(mat1, mat3, sizeof(mat3)); } int main() { int t, c; ll n; while (scanf("%d", &t), t) { for (c = 1; c <= t; c++) { scanf("%I64d", &n); printf("Case %d: ", c); memcpy(mat1, mat1_, sizeof(mat1)); memcpy(mat2, mat2_, sizeof(mat2)); while (n) { if (n & 1) mul(mat1, mat2); mul(mat2, mat2); n >>= 1; } printf("%d\n", mat1[0][0]); } printf("\n"); } }