题意
给出一个
3
∗
p
3*p
3∗p的攻击矩阵,棋子在第
1
1
1行,第
k
k
k列。阅读理解开始了,矩阵是从
0
0
0开始标号的,也就是说棋子在中间那一行,要在一个
n
∗
m
n*m
n∗m的矩阵上摆棋子,问你有多少种摆法能让棋子互相不攻击到。
观察数据范围
n
≤
1
e
6
,
m
≤
6
n \leq 1e6,m \leq 6
n≤1e6,m≤6发现可以状压,显然每一行只跟上一行有关,预处理出这行的摆法下一行能摆什么(位运算),然后矩阵快速幂优化一下就行了,时间复杂度
O
(
m
3
l
o
g
n
)
O(m^3logn)
O(m3logn)
b
a
s
e
[
i
]
[
j
]
=
1
base[i][j]=1
base[i][j]=1表示
j
j
j能摆在
i
i
i的下一行(二进制形式),最后乘上一个
o
n
e
[
1
]
[
1
]
=
1
one[1][1] = 1
one[1][1]=1的矩阵,表示一个虚拟的第零行,第零行下可以摆任意一行,累加得到答案。
代码
#include<bits/stdc++.h>
#define int unsigned long long
#define N 77
#define mod (unsigned long long)(1ull<<32)
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n;i>=a;i--)
#define inf 0x3f3f3f3f
#define pb push_back
#define mp make_pair
#define lowbit(i) ((i)&(-i))
#define VI vector<int>
using namespace std;
int n,m,p,k,tot,atk[3][N],a[3],zt[N],t[N][N];
struct mat{
int M[N][N];
void operator *=(const mat&o){
rep(i,1,tot+1) rep(j,1,tot+1) t[i][j] = 0;
rep(i,1,tot+1){
rep(k,1,tot+1){
rep(j,1,tot+1){
t[i][j] = t[i][j]+(M[i][k]*o.M[k][j])%mod;
t[i][j] %= mod;
}
}
}
rep(i,1,tot+1) rep(j,1,tot+1) M[i][j] = t[i][j];
}
}res,base,one;
signed main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
scanf("%lld%lld%lld%lld",&n,&m,&p,&k);
rep(i,0,3){
rep(j,0,p){
int te;
scanf("%lld",&te); a[i] += (1<<j)*te;
}
}
a[1] -= (1<<k);zt[++tot] = 0;int sum = (1<<m);
rep(i,1,sum){
for(int j = 0,p = i;p;p >>= 1,j++){
if((p&1)==0) continue;
atk[0][i] |= (j<k)?a[0]>>(k-j):a[0]<<(j-k);
atk[1][i] |= (j<k)?a[1]>>(k-j):a[1]<<(j-k);
atk[2][i] |= (j<k)?a[2]>>(k-j):a[2]<<(j-k);
}
}
rep(i,1,sum)
if((i&atk[1][i])==0)
zt[++tot] = i;
// cout << tot << endl;
rep(i,1,tot+1){
rep(j,1,tot+1){
if((atk[2][zt[i]]&zt[j])==0&&(atk[0][zt[j]]&zt[i])==0)
base.M[i][j]++;//,cout << i << ' ' << j << endl;
}
}
rep(i,1,tot+1) res.M[i][i] = 1;one.M[1][1] = 1;
for(;n;n>>=1,base*=base)
if(n&1) res *= base;
one *= res;
int ans = 0;
rep(i,1,tot+1){ans = (ans+one.M[1][i])%mod;} printf("%lld",ans);
return 0;
}