zoukankan      html  css  js  c++  java
  • hdu 4332 Constructing Chimney 夜

    http://acm.hdu.edu.cn/showproblem.php?pid=4332

    啊 又是一道伤不起的题呀   刚开始看了说是状态压缩 dp 然后自己就用滚动数组去写了 超时

    n的大小是 10^9  当然超时 唉又仔细看了一下解题  用什么矩阵快速幂乘  好吧用了一个还是超时

    看了标准代码没看懂呢 愁死了  最后把不同幂的矩阵打表  侥幸过了

    ........................................................ 废话分割线 ..........................................................

    用0表示这个地方需要下面的一层来填充  1表示自己填充 。1的话有可能是平躺着 也有可能是竖着

    上层用下层去更新时   必须满足 (i|j)==((1<<8)-1)和 i&j 不会有连续基数个1 

    前者是因为 上层是0的地方下层必须是1 不能出现上下都是0的情况

    后者是因为 连续基数个1 无法平躺着

    由于后者原因最后取答案是也要有取舍

    还有就是一层上全是1的情况 如果全是平躺着 会有两种方法 其余只有一种

    把每个连乘幂 的矩阵打表记录 然后根据输入对相应的幂矩阵进行相乘

    代码及其注释:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<ctime>
    #include<queue>
    #include<cstring>
    #include<set>
    #include<cmath>
    #include<algorithm>
    #define LL long long
    using namespace std;
    
    const LL MOD=1000000007;
    const int N=300;
    LL ans[N];
    LL matr[31][1<<8][1<<8];//2的几次幂 矩阵
    bool can[N];//代表 是否有连续基数个1 有的话为false  否则为true
    LL t[N];
    int m=1<<8;
    bool OK(int k)//判断是否有连续基数个1
    {
        if(k==0)
        return true;
        int a[10];
        int i;
        for(i=0;i<8;++i)
        {
            a[i]=k%2;
            k=k/2;
        }
        i=0;
        while(a[i])
        ++i;
        for(int j=i+1;j<=i+8;++j)
        {
            if(a[j%8]==1)
            {
                if(a[(j+1)%8]==1)
                {
                    ++j;
                }else
                {
                    return false;
                }
            }
        }
        return true;
    
    }
    void add(int I)//把2的I次幂的矩阵乘到答案数组中
    {
        for(int j=0;j<m;++j)
        {
            t[j]=0;
            for(int i=0;i<m;++i)
            {
                t[j]+=(ans[i]*matr[I][i][j])%MOD;
            }
        }
        for(int i=0;i<m;++i)
        {
            ans[i]=t[i]%MOD;
        }
    }
    void begin()//初始化
    {
        memset(matr,0,sizeof(matr));
        for(int i=0;i<m;++i)
        for(int j=0;j<m;++j)
        if((i|j)==m-1&&can[i&j])
        matr[0][i][j]=1;
        matr[0][m-1][m-1]=2;//初始化 0 次幂矩阵 最后等于2的地方是因为 全是1 有两种摆法
        for(int w=0;w<30;++w)//对不同幂的矩阵 进行打表
        for(int l=0;l<m;++l)
        {
            for(int j=0;j<m;++j)
            {
                matr[w+1][l][j]=0;
                for(int i=0;i<m;++i)
                {
                    matr[w+1][l][j]=(matr[w+1][l][j]+matr[w][l][i]*matr[w][i][j])%MOD;
                }
            }
        }
    
    }
    int main()
    {
       //freopen("data.txt","r",stdin);
       memset(can,false,sizeof(can));
       for(int i=0;i<m;++i)
       if(OK(i))
       can[i]=true;
       else
       can[i]=false;
       begin();
       int T;
       scanf("%d",&T);
       for(int Case=1;Case<=T;++Case)
       {
           int n;
           scanf("%d",&n);
           memset(ans,0,sizeof(ans));
           ans[m-1]=1;//初始化答案数组为n等于1的情况
           --n;//所以连乘时 n减少1
           for(int i=0;n&&i<31;++i)
           {
               if(n%2==1)
               {
                   add(i);
               }
               n=n>>1;
           }
           LL sum=0;
           for(int i=0;i<m;++i)
           if(can[i])
           {
               sum=(sum+ans[i])%MOD;
               if(i==m-1)//由于全是1 会有两种摆法的影响
               sum=(sum+ans[i])%MOD;
           }
           printf("Case %d: ",Case);
           cout<<sum<<endl;
       }
       return 0;
    }
    
  • 相关阅读:
    ARM Linux 3.x的设备树(Device Tree)
    ubuntu 14.04 编译内核出现unable to locate package ncurses-devel 问题的解决
    Device Tree Usage( DTS文件语法)
    Ubuntu 14.04中gedit打开文件出现中文乱码问题
    Jenkins中集成jmeter-maven插件
    Linux(centos6.5)下安装jenkins
    IM系统架构设计之浅见
    一些常用软件的网络端口协议分类介绍
    Jenkins执行批处理文件失败
    八大持续集成工具
  • 原文地址:https://www.cnblogs.com/liulangye/p/2622018.html
Copyright © 2011-2022 走看看