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;
}

 

posted on 2012-08-03 17:35  夜->  阅读(411)  评论(0编辑  收藏  举报