传送门
早该学学状压DP了
可是谁能想到一来就遇到这么毒瘤的题呢
发现
m
m
m很小,可以状压,一行最多64种状态
用
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示第
i
i
i行,状态为
j
j
j的总方案数
若状态
k
k
k能转移到
j
j
j,有转移:
f
[
i
]
[
j
]
=
∑
f
[
i
−
1
]
[
k
]
f[i][j]=\sum f[i-1][k]
f[i][j]=∑f[i−1][k]
考虑优化:
转化为矩阵
矩阵
A
i
,
j
=
1
A_{i,j}=1
Ai,j=1表示
i
→
j
i\to j
i→j转移
考虑转移
i
→
j
→
k
i\to j\to k
i→j→k,显然这是个矩阵乘法
转移
n
n
n次求和得到答案(状态1转移到每个状态)
#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
int i=0,f=1;char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') ch=getchar(),f=-1;
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
typedef unsigned int unt;
const int N=100;
unt n,m,xx[N],yy[N],cnt,sta[N],tot,p,k,result;
struct Mat{
unt mat[N][N];
friend inline Mat operator * (const Mat a,const Mat b){
Mat res;
for(int i=1;i<=64;++i)
for(int j=1;j<=64;++j)
res.mat[i][j]=0;
for(int i=1;i<=64;++i)
for(int j=1;j<=64;++j)
for(int k=1;k<=64;++k)
res.mat[i][j]+=a.mat[i][k]*b.mat[k][j];
return res;
}
}ans,s;
Mat qpw(Mat a,unt b){
Mat res;
res.mat[1][1]=1;
while(b){
if(b&1) res=res*a;
a=a*a;
b>>=1;
}
return res;
}
bool check(int x,int y){
for(int i=0;i<m;++i){
if(x&(1<<i)){
for(int j=1;j<=cnt;++j){
int tmp=i+yy[j];
if(tmp>=m) continue;
if(xx[j]==0){
if(x&(1<<tmp)) return false;
}else if(xx[j]==1){
if(y&(1<<tmp)) return false;
}
}
}
if(y&(1<<i)){
for(int j=1;j<=cnt;++j){
int tmp=i+yy[j];
if(tmp>=m) continue;
if(xx[j]==0){
if(y&(1<<tmp)) return false;
}else if(xx[j]==-1){
if(x&(1<<tmp)) return false;
}
}
}
}
return true;
}
int main(){
n=in,m=in,p=in,k=in;
for(int i=0;i<3;++i)
for(int j=0;j<p;++j){
int x=in;
if(x==1&&(i!=1||j!=k)){
++cnt;
xx[cnt]=i-1;
yy[cnt]=j-k;
}
}
for(int i=0;i<(1<<m);++i)
if(check(i,0)) sta[++tot]=i;
for(int i=1;i<=tot;++i)
for(int j=1;j<=tot;++j)
if(check(sta[i],sta[j])) s.mat[i][j]=1;
ans=qpw(s,n);
for(int i=1;i<=tot;++i)
result+=ans.mat[1][i];
printf("%u\n",result);
return 0;
}