hdu2065--红色病毒

8 篇文章 0 订阅
1 篇文章 0 订阅
医学界发现的新病毒因其蔓延速度和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 看了下网上的题解,大致是下面两种解法。 1.泰勒推导

转载请注明出处,谢谢 http://blog.csdn.net/ACM_cxlove?viewmode=contents           by---cxlove

题源:http://acm.hdu.edu.cn/showproblem.php?pid=2065

比赛的时候遇到这种题,只能怪自己高数学得不好,看着别人秒。。。。

由4种字母组成,A和C只能出现偶数次。

构造指数级生成函数:(1+x/1!+x^2/2!+x^3/3!……)^2*(1+x^2/2!+x^4/4!+x^6/6!……)^2.

前面是B和D的情况,可以任意取,但是相同字母一样,所以要除去排列数。后者是A和C的情况,只能取偶数个情况。

根据泰勒展开,e^x在x0=0点的n阶泰勒多项式为 1+x/1!+x^2/2!+x^3/3!……

而后者也可以进行调整,需要把奇数项去掉,则e^(-x)的展开式为1-x/1!+X^2/2!-X^3/3!……

所以后者可以化简为(e^x+e^(-x))/2。则原式为(e^x)^2   *  ((e^x*e^(-x))/2)^2

整理得到e^4x+2*e^2x+1

又由上面的泰勒展开 

e^4x = 1 + (4x)/1! + (4x)^2/2! + (4x)^3/3! + ... + (4x)^n/n!;

e^2x = 1 + (2x)/1! + (2x)^2/2! + (2x)^3/3! + ... + (2x)^n/n!;

对于系数为n的系数为(4^n+2*2^n)/4=4^(n-1)+2^(n-1);

快速幂搞之。

  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<cstring>  
  4. #include<queue>  
  5. #include<vector>  
  6. #include<cmath>  
  7. #define LL  long long  
  8. #define MOD 100  
  9. #define eps 1e-6  
  10. #define N 100010  
  11. #define zero(a)  fabs(a)<eps  
  12. using namespace std;  
  13. int PowMod(int a,LL b){  
  14.     int ret=1;  
  15.     while(b){  
  16.         if(b&1)  
  17.             ret=(ret*a)%MOD;  
  18.         a=(a*a)%MOD;  
  19.         b>>=1;  
  20.     }  
  21.     return ret;  
  22. }  
  23. int main(){  
  24.     int t;  
  25.     while(scanf("%d",&t)!=EOF&&t){  
  26.         int cas=0;  
  27.         LL n;  
  28.         while(t--){  
  29.             scanf("%I64d",&n);  
  30.             printf("Case %d: %d\n",++cas,(PowMod(4,n-1)+PowMod(2,n-1))%MOD);  
  31.         }  
  32.         printf("\n");  
  33.     }  
  34.     return 0;  
2.递推关系 转载出处 http://blog.csdn.net/welcome_z/article/details/7892353

问题分析 Problem Analyse 递推题

Algorithm Analyse 比起以前做过的递推题,这一题算比较麻烦的了(当然,原因是我没有想到

好的方法,如果你有更方便的方法,欢迎提供大家一起学习)。 如果没有任何条件限制,A、B、C、D组成长度为n的字符串,其个数应该为:4n。

因为有了A、C需要出现偶数次的要求,就出现合法和不合法的不同分组。 在不合法的组里,又有 1.A出现奇数次、C出现偶数次; 2.C出现奇数次、A出现偶数次; 3.A出现奇数次、C出现奇数次; 三种情况。

我们用数组 f[n][0]保存长度为n,合法的字符串的个数。 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] 长度为n-1的合法字符串在末尾加上A,都可以变成长度为n的仅A出现奇数次的字符串。 长度为n-1的仅A出现奇数次的字符串再在末尾加上一个B或者D,也可以变成仅A出现奇数次的字

符串。 长度为n-1的A、C出现奇数次的字符串再在末尾加上一个C,也可以变成仅A出现奇数次的字符串

。 所以,f[n][1] = 2 × f[n-1][1] + f[n-1][0] + f[n-1][3];

f[n][2] 长度为n-1的合法字符串在末尾加上C,都可以变成长度为n的仅C出现奇数次的字符串。 长度为n-1的仅C出现奇数次的字符串再在末尾加上一个B或者D,也可以变成仅C出现奇数次的字

符串。 长度为n-1的A、C出现奇数次的字符串再在末尾加上一个A,也可以变成仅C出现奇数次的字符串

。 所以,f[n][2] = 2 × f[n-1][2] + f[n-1][0] + f[n-1][3];

f[n][3] 长度为n-1的A、C出现奇数次的字符串在末尾加上一B或者D,都可以变成长度为n的A、C出现奇数

次的字符串。 长度为n-1的仅A出现奇数次的字符串再在末尾加上一个C,也可以变成A、C出现奇数次的字符串

。 长度为n-1的仅C出现奇数次的字符串再在末尾加上一个A,也可以变成A、C出现奇数次的字符串

。 所以,f[n][3] = 2 × f[n-1][3] + f[n-1][1] + f[n-1][2];

综上所述,我们得到: 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[1][1]与f[1][2]初始状态相同,而且以后迭代方程也相同,所以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 ∴f[n][0] + f[n][3] = f[n][1] + f[n][2] = 2 × 4^(n-1) ∴f[n-1][1] + f[n-1][2] = 2 × 4^(n-2) ∴f[n][0] = 2 × f[n-1][0] + f[n-1][1] + f[n-1][2] = 2 × f[n-1][0] + 2 × 4^(n-2)

我们得到: f[n][0] = 2 × f[n-1][0] + 2^(2n-3) f[n-1][0] = 2 × f[n-2][0] + 2^(2n-5) ┋ f[n-m][0] = 2 × f[n-m-1][0] + 2^(2n-2m-3) ┋ f[2][0] = 2 × f[1][0] + 2^1 f[1][0] = 2

开始一层层往下迭代: f[n][0] = 2 × f[n-1][0] + 2^(2n-3) = 2^2 × f[n-2][0] + 2^(2n-4) + 2^(2n-3) ┋ = 2^m × f[n-m][0] + 2^(2(n-m)-1+m-1) + … + 2^(2n-3) = 2^(n-1) × f[1][0] + 2^(n-1) + 2^n +… + 2^(2n-3) f[1][0] = 2;

∴f[n][0] = 2^n + 2^(n-1) + 2^n +… + 2^(2n-3) = 2^(2n-2) + 2^(n-1)

算法实现

Programing 公式得到了:f(n) = 2^(2n-2) + 2^(n-1) 但就这样直接编程那是不可能实现的,因为n的范围1≤N<=2^64 怎么的范围,是不能求出f(n)的。所以还得找其他规律。 因为题目只要求输出最后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,与2^2一样,这时候就进入了一个循环了(每20个一次循环)。 所以,结果只能是这22个中的一个。只有n=0 和 n=1是需要特殊考虑的。其他n就等于2(n-2) %20 + 2的值了。

 

  1. #include <iostream>  
  2. using namespace std;  
  3. int p2[30] = {1};  
  4. int po(__int64 x)  
  5. {  
  6.     if (x>1) x = (x - 2)% 20 + 2;  
  7.     if (x<0) return 1;  
  8.     return p2[x];  
  9. }  
  10.   
  11. int main()  
  12. {  
  13.     for (int i=1;i<24;++i) p2[i] = (p2[i-1]<<1) % 100;  
  14.     int t;  
  15.     while (scanf("%d", &t), t)  
  16.     {  
  17.         for (int i=1;i<=t;++i)  
  18.         {  
  19.             __int64 n, a;  
  20.             scanf("%I64d", &n);  
  21.             a = po(2*n-2) + po(n-1);  
  22.             printf("Case %d: %d\n", i, a%100);  
  23.         }  
  24.         printf("\n");  
  25.     }  
  26.     return 0;  
  27. }
两种方法都很好,特地整理收藏起来。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值