HDU-4332-Constructing Chimney

题目描述

\(1*1*2\)的砖头摆出如图所示的烟囱,可以横着摆也可以竖着摆,求摆出\(n\)层高的烟囱会有多少种不同的方案。

Input

一共有\(T\)组数据。

每组数据包含一个\(n(1 \le n \le 1e9)\)

Output

对于每组数据,输出方案数模\(1000000007\)

Sample Input

2
1
2

Sample Output

Case 1: 2
Case 2: 49

看到这个数据范围就是矩阵优化递推了。。

很显然,对于一层的某个状态其中的某个格子放或者是不放可以利用状态压缩来\(DP\)

\(DP\)转移方程为\(dp[i][j]+=dp[i-1][k],j可以转移到k\)

其中,\(dp[i][j]\)表示第\(i\)层,状态为\(j\)的方案数。

那么,暴力转移的复杂度为\(O(n*256^2)\)

由于每层的转移是唯一的,所以利用矩阵优化。

对于一个状态\(i\)我们暴力枚举其他状态\(j\)\(check\)能不能相进行转移。

若能转移\(g_i,_j\)\(1\),否则为\(0\)

构造出转移矩阵\(g=\)\(\begin{pmatrix} 0 & 0 & 1 & \cdots & 1 \\ 1 & 0 & 0 & \cdots & 0 \\ 0 & 1 & 0 & \cdots & 0 \\\vdots & \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & 0 & \cdots & 0 \\ \end{pmatrix}\),利用矩阵快速幂求解。

时间复杂度降为\(O(log_n*256^3)\)

有点小大,还有多组数据,不是很稳!!!

那么考虑剪枝。

我们把那张\(256*256\)的表打出来,发现矩阵的大部分是\(0\),也就是说,有大部分的状态是无用的,也就是说一些状态是永远也更新不到的,我们可以把这些状态去掉。

我们发现那些只有每层奇数个格子有的状态是无用的,可以直接少掉一半的复杂度,时间复杂度\(O(log_n*128^3)\)

卡一卡常还是能过的。

其实还可以在进行优化,机房大佬\(gxy\)直接把代码优化到\(0ms\)

据说是先缩到\(128*128\),再四个方向旋转去重,再对称和镜像去重减到只有十几种情况,打一个表解决。。。

orzorz...

代码如下

#include <bits/stdc++.h>

using namespace std;

#define LL long long
#define u64 unsigned long long
#define u32 unsigned int
#define reg register
#define Raed Read
#define debug(x) cerr<<#x<<" = "<<x<<endl;
#define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a)
#define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a)
#define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a)
#define erep(i,x) for(reg int i=Head[x]; i; i=Nxt[i])

inline int Read() {
	int res = 0, f = 1;
	char c;
	while (c = getchar(), c < 48 || c > 57)if (c == '-')f = 0;
	do res = (res << 3) + (res << 1) + (c ^ 48);
	while (c = getchar(), c >= 48 && c <= 57);
	return f ? res : -res;
}

template<class T>inline bool Min(T &a, T const&b) {
	return a > b ? a = b, 1 : 0;
}
template<class T>inline bool Max(T &a, T const&b) {
	return a < b ? a = b, 1 : 0;
}

const int N=305,M=1e5+5,mod=1e9+7;

bool MOP1;

int n,m,cnt,Id[N];

struct Matrix {
	int Num[N][N];
	inline void clear(void) {
		memset(Num,0,sizeof Num);
	}
	inline void Init(void) {
		rep(i,1,cnt)Num[i][i]=1;
	}
	inline Matrix operator*(Matrix _)const {
		Matrix Ans;
		Ans.clear();
		rep(i,1,cnt)rep(j,1,cnt)rep(k,1,cnt)Ans.Num[i][j]=(Ans.Num[i][j]+1ll*Num[i][k]*_.Num[k][j])%mod;
		return Ans;
	}
} us;

inline Matrix qpow(Matrix A,int k) {
	Matrix res;
	res.clear(),res.Init();
	while(k) {
		if(k&1)res=res*A;
		A=A*A,k>>=1;
	}
	return res;
}

int Sum(int x) {
	int tot=0;
	while(x)x^=x&-x,tot++;
	return tot;
}

bool check(int x,int y) {
	int TT=x|y;
	if(TT!=255)return false;
	TT=x&y;
	if(TT==255)return true;
	while(TT&1)TT=1<<7|(TT>>1);
	int tot=0;
	rep(i,0,8) {
		if(i<8&&(TT>>i)&1)tot++;
		else if(tot&1)return false;
	}
	return true;
}

bool MOP2;

inline void _main(void) {
//  cerr<<"M="<<(&MOP2-&MOP1)/1024.0/1024.0<<endl;
	int T=Raed(),m=1<<8;
	us.clear();
	ret(i,0,m) {
		if(Sum(i)&1)continue;
		Id[i]=++cnt;
	}
	ret(i,0,m)if(Id[i])ret(j,0,m)if(Id[j]&&check(i,j))us.Num[Id[i]][Id[j]]=1;
	us.Num[Id[255]][Id[255]]++;
	int Case=0;
	while(T--) {
		int n=Read();
		Matrix Ans=qpow(us,n);
		printf("Case %d: %d\n",++Case,Ans.Num[Id[255]][Id[255]]);
	}
}

signed main() {
#define offline1
#ifdef offline
	freopen("chessboard.in", "r", stdin);
	freopen("chessboard.out", "w", stdout);
	_main();
	fclose(stdin);
	fclose(stdout);
#else
	_main();
#endif
	return 0;
}
posted @ 2019-08-08 21:13  dsjkafdsaf  阅读(180)  评论(0编辑  收藏  举报