题目中的标号都是从0开始的!所以攻击模板中你的位置为中间那一行!所以可以只考虑相邻两行的转移!
先处理出f[s1][s2]表示相邻两行状态为s1,s2是否可行。
然后n很大,所以我们要矩阵加速。
复杂度
O((2m)3logn)
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 70
#define uint unsigned int
inline char gc(){
static char buf[1<<16],*S,*T;
if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(S==T) return EOF;}
return *S++;
}
inline int read(){
int x=0,f=1;char ch=gc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
return x*f;
}
int n,nn,m,bin[10];
vector<int>mp[3];
struct Matrix{
uint a[N][N];
Matrix(){}
Matrix(bool t){memset(a,0,sizeof(a));if(t) for(int i=0;i<=n;++i) a[i][i]=1;}
friend Matrix operator*(Matrix a,Matrix b){
Matrix res(0);
for(int i=0;i<=n;++i)
for(int j=0;j<=n;++j)
for(int k=0;k<=n;++k)
res.a[i][j]+=a.a[i][k]*b.a[k][j];return res;
}friend Matrix operator^(Matrix a,int k){
Matrix res(1);
for(;k;k>>=1,a=a*a) if(k&1) res=res*a;return res;
}
}trans,a;
inline bool jud(int s1,int s2){
for(int i=0;i<m;++i){
if(!(bin[i]&s1)) continue;
for(int j=0;j<mp[1].size();++j){
int x=mp[1][j]+i;if(x<0||x>=m) continue;
if(bin[x]&s1) return 0;
}for(int j=0;j<mp[2].size();++j){
int x=mp[2][j]+i;if(x<0||x>=m) continue;
if(bin[x]&s2) return 0;
}
}for(int i=0;i<m;++i){
if(!(bin[i]&s2)) continue;
for(int j=0;j<mp[1].size();++j){
int x=mp[1][j]+i;if(x<0||x>=m) continue;
if(bin[x]&s2) return 0;
}for(int j=0;j<mp[0].size();++j){
int x=mp[0][j]+i;if(x<0||x>=m) continue;
if(bin[x]&s1) return 0;
}
}return 1;
}
int main(){
// freopen("a.in","r",stdin);
nn=read();m=read();int p=read(),k=read();bin[0]=1;
for(int i=1;i<=m;++i) bin[i]=bin[i-1]<<1;n=bin[m]-1;
for(int i=0;i<3;++i){
for(int j=0;j<p;++j){
int x=read();if(i==1&&j==k) continue;
if(x) mp[i].push_back(j-k);
}
}for(int s1=0;s1<=n;++s1)
for(int s2=0;s2<=n;++s2) trans.a[s1][s2]=jud(s1,s2);
a.a[0][0]=1;a=a*(trans^nn);uint ans=0;
for(int i=0;i<=n;++i) ans+=a.a[0][i];
printf("%u\n",ans);
return 0;
}