题目链接
题目大意
有一排砖。数量为N。现要将砖所有染上色。有红、蓝、绿、黄四种颜色。
要求被染成红色和绿色的砖块数量必须为偶数,问一共同拥有多少种染色方案。(因为答案较大。模10007)
矩阵快速幂思路
//白书202面
组合数学思路
emm感觉这个组合数学有点硬核
前置知识
C(n,0)+C(n,1)+…+C(n,n)=2^n
证明方法1:
观察这个公式,实际上我们可以理解成一个分类加法计数原理,从这 n 个元素中选择 0,1,2,……,n 个元素的所有情况,其实相当于讨论每个元素选或不选,然后用分步乘法计数原理进行统计。每个元素有两种选择,所以是 2^n。也就是说,左右两边相等。
证明方法2:
使用二项式定理(1+1)^n展开一下即可
c(n,m)=c(n-1,m-1)+c(n-1,m)
正文
如果没有限制,一共有4 ^ n 次。现在考虑有 k 块被染为红色或绿色,且在k块中,一定有红色或绿色或两者均为奇数的情况。将这些情况减去,即是想要的答案。(1<= k <= n)
从n块中选择k块,为c(n, k)。 而从k块中选择不符合的情况染色,需要对k进行奇偶讨论。
如果k为奇数,红色和绿色的数量为一奇一偶:2 * (c(k, 1) + c(k, 3) + c(k, 5) +……)* c(n, k) * 2^(n - k) (其中要乘以2,是因为可以分别选择红、绿色为奇数)
如果k为偶数,红色和绿色的数量全部为奇数: (c(k, 1) + c(k, 3) + c(k, 5) +……)* c(n, k) * 2^(n - k) (这里不需要乘以2)
而 c(k, 1) + c(k, 3) + c(k, 5) +…… = 2^(k - 1)
所以当k为偶数时为2^(n-1) *c(n,k)
2^(n-1) ( c(n,2)+c(n,4)+c(n,6)+…) 等于2^(n-1) (2^(n-1)-1)//减一是因为没有c(n,0)
当k为奇数时为2^n *c(n,k)
2^(n)( c(n,1)+c(n,3)+c(n,5)+…)等于2^n (2^(n-1))
答案为4^n -2^(2n-2) + 2^(n-1) -2^ (2n-1)= 4^(n-1)+ 2^(n-1)
矩阵快速幂代码
#include<cstdio>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn=1e2+10,mod=1e4+7;
int t,n,k,base[maxn][maxn],ans[maxn][maxn],temp[maxn][maxn];
void mul1(){
for(int i=1;i<=3;i++){
for(int j=1;j<=3;j++){
temp[i][j]=ans[i][j];
ans[i][j]=0;
}
}
for(int i=1;i<=3;i++){
for(int j=1;j<=3;j++){
for(int k=1;k<=3;k++){
ans[i][j]=(ans[i][j]+base[i][k]*temp[k][j])%mod;
}
}
}
}
void mul2(){
for(int i=1;i<=3;i++){
for(int j=1;j<=3;j++){
temp[i][j]=base[i][j];
base[i][j]=0;
}
}
for(int i=1;i<=3;i++){
for(int j=1;j<=3;j++){
for(int k=1;k<=3;k++){
base[i][j]=(base[i][j]+temp[i][k]*temp[k][j])%mod;
}
}
}
}
void qpow(ll b){
while(b>0){//注意要写>0,因为可能是负数
if(b&1){
mul1();//ans*base
}
b=b>>1;
mul2();//base自乘
}
}
int main(){
scanf("%d",&t);
while(t--){
ans[1][1]=ans[2][1]=ans[2][2]=ans[2][3]=ans[3][3]=2;
ans[1][2]=ans[3][2]=1;
ans[1][3]=ans[3][1]=0;
for(int i=1;i<=3;i++){
for(int j=1;j<=3;j++){
base[i][j]=ans[i][j];
}
}
scanf("%d",&n);
qpow(n-2);
if(n==1){
printf("2\n");
}else{
printf("%d\n",(2*(ans[1][1]+ans[1][2]))%mod);
}
}
return 0;
}
组合数学
#include<cstdio>
using namespace std;
const int mod=1e4+7;
int t,n,ans;
int qpow(int a,int b){
int ans=1,base=a;
while(b>0){
if(b&1){
ans=(ans*base)%mod;
}
b=b>>1;
base=(base*base)%mod;
}
return ans;
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d",&n);
ans=qpow(2,n-1);
ans=ans*(ans+1)%mod;
printf("%d\n",ans);
}
return 0;
}