• POJ 3734 Blocks


                                                             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
    ",(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)。

    初始矩阵M0就是:(1,0,0,0),即刚才所说的那四个状态的值。容易求出操作矩阵A是:
    2 1 1 0
    1 2 0 1
    1 0 2 1
    0 1 1 2
    所以最终矩阵Mn=M0×A^n,取Mn的第一个元素即为所求。
    #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
    ", ans.a[0][0]);
        }
        return0;
    }
  • 相关阅读:
    搭建一个属于私人博客
    Python正则表达式的匹配规则
    CentOS 使用yum 安装node.js
    一个单词a,如果通过交换单词中字母的顺序可以得到另外的单词b,那么定义b是a的兄弟单词。现在有一个字典,用户输入一个单词,从字典找出这个单词有多少个兄弟单词
    Clion报错 CMake Error at CMakeLists.txt:1 (cmake_minimum_required): CMake 3.
    给定一个整数sum,从n个有序的元素的数组中寻找a,b,使得a+b的结果最接近sum,最快的时间复杂度?
    Go语言通过Docker Go语言SDK获取docker stats的信息
    通过GO程序获取docker version的基本信息
    Go语言实现通过Docker SDK获取docker ps 命令信息&SDK 中docker ps源码解析
    Docker监控docker stats命令的使用与返回参数的意思
  • 原文地址:https://www.cnblogs.com/wkxnk/p/3913618.html
Copyright © 2020-2023  润新知