hdu2065--红色病毒

来源:互联网 发布:工商银行网络银行 编辑:程序博客网 时间:2024/04/30 12:13
医学界发现的新病毒因其蔓延速度和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

41420113142460
 

Sample Output

Case 1: 2Case 2: 72Case 3: 32Case 4: 0Case 1: 56Case 2: 72Case 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);

快速幂搞之。

[cpp] view plaincopy
  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] = 2f[1][1] = 1f[1][2] = 1f[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^1f[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  12^1  22^2  42^3  82^4  162^5  322^6  642^7  282^8  562^9  122^10  242^11  482^12  962^13  922^14  842^15  682^16  362^17  722^18  442^19  882^20  762^21  522^22  4到了2^22时,末尾2位又变成4,与2^2一样,这时候就进入了一个循环了(每20个一次循环)。所以,结果只能是这22个中的一个。只有n=0 和 n=1是需要特殊考虑的。其他n就等于2(n-2) %20 + 2的值了。

 

[cpp] view plaincopyprint?
  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. }
两种方法都很好,特地整理收藏起来。
0 0
原创粉丝点击