HDU2065

Problem Description
医学界发现的新病毒因其蔓延速度和Internet上传播的"红色病毒"不相上下,被称为"红色病毒",经研究发现,该病毒及其变种的DNA的一条单链中,胞嘧啶,腺嘧啶均是成对出现的。
现在有一长度为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");
    }
}



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值