现在有一长度为N的字符串,满足一下条件:
(1) 字符串仅由A,B,C,D四个字母组成;
(2) A出现偶数次(也可以不出现);
(3) C出现偶数次(也可以不出现);
计算满足条件的字符串个数.
当N=2时,所有满足条件的字符串有如下6个:BB,BD,DB,DD,AA,CC.
由于这个数据肯能非常庞大,你只要给出最后两位数字即可.
Input
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.youkuaiyun.com/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);
快速幂搞之。
2.递推关系 转载出处 http://blog.youkuaiyun.com/welcome_z/article/details/7892353
- #include<iostream>
- #include<cstdio>
- #include<cstring>
- #include<queue>
- #include<vector>
- #include<cmath>
- #define LL long long
- #define MOD 100
- #define eps 1e-6
- #define N 100010
- #define zero(a) fabs(a)<eps
- using namespace std;
- int PowMod(int a,LL b){
- int ret=1;
- while(b){
- if(b&1)
- ret=(ret*a)%MOD;
- a=(a*a)%MOD;
- b>>=1;
- }
- return ret;
- }
- int main(){
- int t;
- while(scanf("%d",&t)!=EOF&&t){
- int cas=0;
- LL n;
- while(t--){
- scanf("%I64d",&n);
- printf("Case %d: %d\n",++cas,(PowMod(4,n-1)+PowMod(2,n-1))%MOD);
- }
- printf("\n");
- }
- return 0;
- }
问题分析 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的值了。
两种方法都很好,特地整理收藏起来。
- #include <iostream>
- using namespace std;
- int p2[30] = {1};
- int po(__int64 x)
- {
- if (x>1) x = (x - 2)% 20 + 2;
- if (x<0) return 1;
- return p2[x];
- }
- int main()
- {
- for (int i=1;i<24;++i) p2[i] = (p2[i-1]<<1) % 100;
- int t;
- while (scanf("%d", &t), t)
- {
- for (int i=1;i<=t;++i)
- {
- __int64 n, a;
- scanf("%I64d", &n);
- a = po(2*n-2) + po(n-1);
- printf("Case %d: %d\n", i, a%100);
- }
- printf("\n");
- }
- return 0;
- }