POJ 3734 Blocks 题解(矩阵快速幂or组合数学+快速幂)

题目链接

题目大意

有一排砖。数量为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;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值