CCF CSP 201409-5 拼图
在写这题时,先后根据不同博客学习了状压dp、矩阵快速幂,最后代码有些小bug参考了一位思路差不多的大佬以做出修改,在这里先感谢三位大佬,之后会在思路里贴出他们的博文链接;
知乎上好多人喷CSDN怎样怎样,我觉得静下心来,在这一篇篇博文里还是能学到很多东西的嘛…
思路:
1.使用状态压缩动态规划来求递推式;
2.不会状压dp的请看这位大佬的博客: https://blog.csdn.net/u011077606/article/details/43487421.大家可以认真学习博主在最前面提到的位运算,因为用一个int各位上的0/1来压缩状态的表示就是状压dp的特色。这位博主例1的问题与http://poj.org/problem?id=2411几乎一样,大家学习后可以用这道简单的例题来测试自己有没有掌握状压dp。
3.这题和上面那位博主的例一有点像,只是将长方体换成了形状不一样的积木;另外最重要的就是n
的范围达到了1015,如果用纯状压dp求解只能70分;怎样加速求解呢?这里我们可以采用矩阵快速幂;
4.在学习矩阵快速幂之前,应该先学习快速幂的思想;然后再去学矩阵快速幂,这里是传送门:https://blog.csdn.net/wust_zzwh/article/details/52058209.在仔细学习后,我们会明白,只要求一个转移矩阵,再将其求n次方,最后乘以初始行列式即可。在求n次方的时候,就可以用快速幂思想了!
5.最后贴一下另一位大佬的链接,正是靠着他的代码,我才得以逐步挑出自己代码中的小bug:https://blog.csdn.net/vcvycy/article/details/78243162.
代码:
#include<bits/stdc++.h>
using namespace std;
#define rp(i,n) for(int i=0;i<n;i++)
typedef long long ll;
const int MOD=1000000007;
const int M=(1<<7)+5;
ll n;
int m;
int mm; //存放2^m
#define VACANT(i,s) ((i>=0)&&(i<m)&&(!((1<<i)&s)))//状态为S的列第i行是否为空
int t[M][M];//转移矩阵 i,j各代表状态为j时,下一列状态为i的个数
void dfs(int i,int state,int nex){//处理当前第i行的情况
if(i==m){
t[nex][state]++;
return;
}
if(!VACANT(i,state)) dfs(i+1,state,nex); //此格子被占用
if(VACANT(i,state)&&VACANT(i,nex)&&VACANT(i-1,nex)){//符合插入第一种积木的条件
dfs(i+1,state,nex|(1<<i)|(1<<(i-1)));
}
if(VACANT(i,state)&&VACANT(i,nex)&&VACANT(i+1,nex)){//符合插入第二种积木的条件
dfs(i+1,state,nex|(1<<i)|(1<<(i+1)));
}
if(VACANT(i,state)&&VACANT(i+1,state)&&VACANT(i+1,nex)){//符合插入第三种积木的条件
dfs(i+2,state,nex|(1<<(i+1)));
}
if(VACANT(i,state)&&VACANT(i+1,state)&&VACANT(i,nex)){//符合插入第四种积木的条件
dfs(i+2,state,nex|(1<<i));
}
}
int temp[M][M];
void multi(int a[][M],int b[][M]){//计算矩阵(a*b)%MOD,结果存放在a中
memset(temp,0,sizeof(temp));//每次初始化,因为下面是+=运算符
rp(i,mm) rp(j,mm) rp(k,mm){
temp[i][j]=(temp[i][j]+1ll*a[i][k]*b[k][j]%MOD)%MOD;
}
rp(i,mm) rp(j,mm) a[i][j]=temp[i][j];
}
int res[M][M];
void Pow(int a[][M],ll n){//快速幂求(a^n),结果存放在矩阵res中
rp(i,M) res[i][i]=1;//初始化res=1;
while(n){
if(n&1) multi(res,a);//n为奇数
multi(a,a);//a=a^2;
n>>=1; //n/=2;
}
}
int main(){
cin>>n>>m;
mm=(1<<m);
for(int i=0;i<=mm;i++) dfs(0,i,0);//搜索状态为i的列的下一列可以是什么状态
Pow(t,n); //转移矩阵自乘n次;
cout<<res[0][0]; //初始行列式为[1;0;0;...;0];
return 0;
}