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;
}
浙公网安备 33010602011771号