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