其实这道题做的我挺难受的…. 看不太懂该怎么搞.网上的东西也都讲的很简单
首先这个题发现 m m 很小,考虑把每一行状压下来
预处理出哪一行能转移到哪一行
这个东西我不太会搞,还是看的的代码…
就是你对于当前枚举的两个状态,每找到一个
1
1
,把棋子的位置强行平移到这个
的位置上来, 然后判一下相邻的状态就好了
直接做发现 1e6 1 e 6 的数据范围是过不了的,我们考虑优化,发现每一行转移的都是相同的。通俗一点可以这样讲,如果状态 S S 的后继状态集合为, 那么无论是哪一行的状态为 S S 时,它都能够转移到的每一个状态中,这样子就可以用矩阵快速幂优化了,考虑怎么构造转移矩阵,其实预处理的时候就可以直接构造好了,如果 i i 状态能转移到状态,那么转移矩阵 (i,j) ( i , j ) 这个位置对应的系数就是 1 1
复杂度
Codes
#include<bits/stdc++.h>
#define int unsigned int
using namespace std;
const int N = (1 << 6) + 3;
int n, m, p, k;
int a[N], all, ans;
struct Martix {
int A[N][N];
Martix operator * (const Martix &T) {
Martix res;
for(int i = 0; i < all; ++ i)
for(int j = 0; j < all; ++ j) {
res.A[i][j] = 0;
for(int q = 0; q < all; ++ q)
res.A[i][j] += A[i][q] * T.A[q][j];
}
return res;
}
}A1, A2;
void qpow(int x) {
while(x) {
if(x & 1) A2 = A2 * A1;
x >>= 1, A1 = A1 * A1;
}
}
bool check(int S1, int S2) {
int pre, now, sub;
for(int i = 0; i < m; ++ i) {
if((1 << i) & S1) {
if(i > k) now = a[1] << (i - k), sub = a[2] << (i - k);
else now = a[1] >> (k - i), sub = a[2] >> (k - i);
if((S1 & now) || (S2 & sub)) return false;
}
if((1 << i) & S2) {
if(i > k) now = a[1] << (i - k), pre = a[0] << (i - k);
else now = a[1] >> (k - i), pre = a[0] >> (k - i);
if((S2 & now) || (S1 & pre)) return false;
}
}
return true;
}
signed main() {
#ifndef ONLINE_JUDGE
freopen("3977.in", "r", stdin);
freopen("3977.out", "w", stdout);
#endif
scanf("%u%u%u%u", &n, &m, &p, &k);
for(int i = 0, x; i < 3; ++ i)
for(int j = 0; j < p; ++ j)
scanf("%d", &x), a[i] |= x << j;
all = 1 << m; a[1] ^= 1 << k;
for(int i = 0; i < all; ++ i)
for(int j = 0; j < all; ++ j)
A1.A[i][j] = check(i, j);
for(int i = 0; i < all; ++ i)
A2.A[i][i] = 1;
qpow(n);
for(int i = 0; i < all; ++ i)
ans += A2.A[0][i];
cout << ans << endl;
return 0;
}
位运算相关的东西自己知道的还是少了点啊…
以后这种题还是独立做下吧..