首先对于这一类题目时存在一个
O
(
(
n
m
)
3
)
O((nm)^3)
O((nm)3)的暴力的。
假设每一个格子是一个变量,1表示选中,0则没有。
根据题意可以列出若干个异或方程组,要求每一个格子异或出来都要等于1。高斯消元可以在
O
(
(
n
m
)
3
)
O((nm)^3)
O((nm)3)内做出,答案则是
2
自
由
元
个
数
2^{自由元个数}
2自由元个数
然后考虑以下的问题:
有一个n*m 的网格,每一个格子最初时白色,你每一次选中一个格子,将它和它的四个邻居(如果存在的话)全部颜色反转,找到一种方案。
显然,如果n/m比较小时可以暴力枚举第一行/第一列。然后往后的每一行/每一列,根据前面的那一格而定。也就是说第一行确定,下面的所有点的方案也就确定了。这样可以做到
O
(
2
n
∗
m
)
O(2^n*m)
O(2n∗m)虽然这个暴力远不如高斯消元的
O
(
(
n
∗
m
)
3
)
O((n*m)^3)
O((n∗m)3)但我们可以从中获得启发。
其实这个问题也有这样的性质,如果从上往下从左往右的选择,如果当前的格子为
(
i
,
j
)
(i,j)
(i,j),则当且仅当
(
i
−
2
,
j
−
1
)
=
0
(i-2,j-1)=0
(i−2,j−1)=0时才选择(i,j)。
这样我们就可以搞出每一个格子是否选择与前两行和第一列中格子的关系。
因为如果一个格子
(
i
,
j
)
(i,j)
(i,j)存在一个格子
(
i
+
2
,
j
+
1
)
(i+2,j+1)
(i+2,j+1)则
(
i
,
j
)
(i,j)
(i,j)一定是黑色。所以只需要考虑没有这些点的格子。也就是最后两行和最后一列。
用之前算出的关系来列方程可以做到
O
(
(
n
+
m
)
3
+
n
∗
m
∗
(
n
+
m
)
/
64
)
O((n+m)^3+n*m*(n+m)/64)
O((n+m)3+n∗m∗(n+m)/64)
code:
/*
{
AuThOr Gwj
*/
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
const int MAXN=155;
const int MOD=123456789;
typedef pair<bool,bitset<MAXN*3> > data ;
data inf[MAXN][MAXN];
int n,m;
int dire[8][2]={{1,2},{1,-2},{-1,2},{-1,-2},{2,1},{-2,1},{2,-1},{-2,-1}};
bool equ[550][550];
int Gauss(){
int rest=0;
int las=1;
// rb(i,1,)
rb(j,1,m-1){
bool ok=0;
rb(i,las,n){
if(equ[i][j]){
ok=1;
swap(equ[las++],equ[i]);
break;
}
}
if(!ok){
rest++;
continue;
}
rb(i,1,n){
if(las-1==i) continue;
if(!equ[i][j]) continue;
rb(k,1,m)
equ[i][k]^=equ[las-1][k];
}
}
rb(i,las,n)
if(equ[i][m]) rest=-1;
return rest;
}
int id[1000000],id2[1000000];
class KnightsOut{
public:
int count(int N, int M){
int rest=0;
int res=1;
if(N*M<=500){
n=m=N*M;
rb(i,1,N)
rb(j,1,M){
int ni,nj;
rep(k,8){
ni=i+dire[k][0];
nj=j+dire[k][1];
if(ni>=1&&ni<=N&&nj>=1&&nj<=M){
equ[(ni-1)*M+nj][(i-1)*M+j]=1;
}
}
equ[(i-1)*M+j][(i-1)*M+j]=1;
}
m++;
rb(i,1,n)
equ[i][m]=1;
rest=Gauss();
}
else{
int cnt=0;
rb(i,1,2)
rb(j,1,M){
id[(i-1)*M+j]=++cnt;
}
rb(i,3,N)
id[(i-1)*M+1]=++cnt;
rb(i,1,2)
rb(j,1,M){
inf[i][j].FIR=0;
inf[i][j].SEC.set(id[(i-1)*M+j]);
}
rb(i,3,N)
inf[i][1].FIR=0,inf[i][1].SEC.set(id[(i-1)*M+1]);
rb(i,1,N)
rb(j,1,M){
if(i<=2) continue;
if(j==1) continue;
int li,lj;
li=i-2;
lj=j-1;
inf[i][j].FIR=1;
rep(k,8){
int ni,nj;
ni=li+dire[k][0];
nj=lj+dire[k][1];
if(ni==i&&nj==j) continue;
if(ni<1||ni>N||nj<1||nj>M) continue;
inf[i][j].FIR^=inf[ni][nj].FIR;
inf[i][j].SEC^=inf[ni][nj].SEC;
}
inf[i][j].FIR^=inf[li][lj].FIR;
inf[i][j].SEC^=inf[li][lj].SEC;
}
m=cnt;
rb(i,N-1,N)
rb(j,1,M)
{
n++;
bitset<MAXN*3> bbs;
bool fli=0;
bbs=inf[i][j].SEC;
fli=inf[i][j].FIR;
rep(k,8)
{
int ni,nj;
ni=dire[k][0]+i;
nj=dire[k][1]+j;
if(ni>=1&&ni<=N&&nj>=1&&nj<=M){
bbs^=inf[ni][nj].SEC;
fli^=inf[ni][nj].FIR;
}
}
rb(k,1,cnt)
if(bbs[k]){
equ[n][k]=1;
}
if(!fli){
equ[n][m+1]=1;
}
}
rb(i,1,N-2)
{
n++;
bitset<MAXN*3> bbs;
bool fli=0;
bbs=inf[i][M].SEC;
fli=inf[i][M].FIR;
rep(k,8)
{
int ni,nj;
ni=dire[k][0]+i;
nj=dire[k][1]+M;
if(ni>=1&&ni<=N&&nj>=1&&nj<=M){
bbs^=inf[ni][nj].SEC;
fli^=inf[ni][nj].FIR;
}
}
rb(k,1,cnt)
if(bbs[k]){
equ[n][k]=1;
}
if(!fli){
equ[n][m+1]=1;
}
}
m++;
// cout<<n<<" "<<m<<endl;
rest=Gauss();
// cout<<rest<<endl;
}
if(rest==-1) res=0;
else
rb(i,1,rest)
res<<=1,res%=MOD;
return res;
}
};
//KnightsOut solver;
//int main(){
// fastio;
// cout<<solver.count(
//69,
//142
//)<<endl;
// return 0;
//}