POJ 3734 Blocks
Time Limit:1000MS Memory Limit:65536KB 64bit IO Format:%I64d & %I64u
Description
Panda has received an assignment of painting a line of blocks. Since Panda is such an intelligent boy, he starts to think of a math problem of painting. Suppose there are N blocks in a line and each block can be paint red, blue, green or yellow. For some myterious reasons, Panda want both the number of red blocks and green blocks to be even numbers. Under such conditions, Panda wants to know the number of different ways to paint these blocks.
Input
The first line of the input contains an integer T(1≤T≤100), the number of test cases. Each of the next T lines contains an integer N(1≤N≤10^9) indicating the number of blocks.
Output
For each test cases, output the number of ways to paint the blocks in a single line. Since the answer may be quite large, you have to module it by 10007.
Sample Input
2 1 2
Sample Output
2 6
题目大意:有n个方块排成一排,现在要给它们涂色,有四种颜色可以选择:red,green,blue,yellow。要求涂red和green的方块个数为偶数,问有多少种涂色方案。
分析:总的来说有两种方法:一、利用组合计数的方法推导出通项公式;二、推出递推公式,用DP求解。
通项公式推导:
现在我们将空格分成两个部分: 1.用蓝黄两种颜色上色 2.用红绿两种颜色上色。
前面的部分上色的方法数:2^(n-k)。(k为偶数,k > 0)其他部分上色的方法数位:C(k,0)+C(k,2)+…C(K,t)+C(k,k) = 2^(k-1).(二项式系数的偶数项之和与奇数项之和相等)
这样我们可以当K钟颜色上红绿的方法数:2^(n-k) * 2^(k-1) = 2^(n -1).
那么我们得到中的方法数数:
ans = C(n,0)*2^(n)+C(n,2)*2^(n-1)+C(n,4)*2^(n-1)+… C(n,k) *2^(n-1) ….
= 2^(n-1)* (2+C(n,2)+C(n,4)+… C(n,k) …)
= 2^(n-1) *( 1+C(n,0)+C(n,2)+C(n,4)+… C(n,k) …)
= 2^(n-1) *(1+2*(n-1) )
= 2^(n-1)+4^(n-1)
特别的,当n=0,ans=0; 当n=1,ans=2。
在求一般式2^(n-1)+4^(n-1)时,可以用快速幂优化,甚至可以用费马小定理进一步优化。
以下是 组合计数+快速幂:
#include<cstio> using namespace std; int mul(int a,int b) { int temp = 1; while(b) { if(b & 1) { temp = (temp * a)%10007; } b = b >> 1; a = (a*a) % 10007; } return temp; } int main() { int n,tes; scanf("%d",&tes); while(tes--) { scanf("%d",&n); printf("%d\n",(mul(4,n-1) mul(2,n-1))%10007); } return 0; }
费马小定理的内容是:若p为素数,则对于任意小于p的正整数a,有a^p≡a(mod p),如果a,p互质,则可以进一步化为a^(p-1)≡1(mod p)。
可推出性质:a^m≡a^(m mod (p-1))(mod p)。
以下是 组合计数+二分递归+费马小定理:
#include<iostream> #include<fstream> #include<cmath> using namespace std; int solve(int s){ int i,j,k; if(s==1) return 2; if(s==0) return 1; i=solve(s/2); if(s%2) { return (i*i*2)%10007; } else return (i*i)%10007; } void read(){ int i,j,k,s; cin>>k; while(k--) { cin>>i; j=solve((i-1)%10006); s=solve((2*(i-1))%10006); cout<<(j+s)%10007<<endl; } } int main(){ read(); return 0; }
对于DP求递推,考虑到此时一般的DP会超时,应该用矩阵快速幂优化之。
R0表示Red填了偶数个,R1表示Red填了奇数个,G0表示Green填了偶数个,G1表示Green填了奇数个。每次我们就是让(R0,G0),(R0,G1),(R1,G0),(R1,G1)这四种状态转移。显然初始的时候(R0,G0)的值为1,最终要求的答案也是(R0,G0)。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> usingnamespace std; #define maxn 4 #define w 10007 struct Matrix { int a[maxn][maxn]; int x, y; }ans, f; int n; int matrix[maxn][maxn]={{2,1,1,0},{1,2,0,1},{1,0,2,1},{0,1,1,2}}; Matrix mul(Matrix &a, const Matrix &b) { Matrix ret; for (int i =0; i < a.x; i++) for (int j =0; j < b.y; j++) { int d =0; for (int k =0; k < a.y; k++) d = (d + a.a[i][k] * b.a[k][j] % w) % w; ret.a[i][j] = d; } ret.x = a.x; ret.y = b.y; return ret; } Matrix power(Matrix m, int n) { Matrix ret; memset(ret.a, 0, sizeof(ret.a)); for (int i =0; i <4; i++) ret.a[i][i] =1; ret.x = ret.y =4; for (int k =1; k <= n; k <<=1) { if (k & n) ret = mul(ret, m); m = mul(m, m); } return ret; } int main() { #ifndef ONLINE_JUDGE freopen("t.txt", "r", stdin); #endif f.x = f.y =4; for (int i =0; i <4; i++) for (int j =0; j <4; j++) f.a[i][j] = matrix[i][j]; int t; scanf("%d", &t); while (t--) { scanf("%d", &n); f.x = f.y =4; memset(ans.a, 0, sizeof(ans.a)); ans.a[0][0] =1; ans.x =1; ans.y =4; ans = mul(ans, power(f, n)); printf("%d\n", ans.a[0][0]); } return0; }