TC8744 - KnightsOut 题解
(今天 vj 挂掉了,但是意外找到国内镜像站,以后再也不用担心 vj 挂掉啦!)
把每个格子选或不选当变量,对每个格子列异或方程组,得到一个 \(nm\times nm\) 的异或方程。高消硬解复杂度是 \(\mathrm O\!\left((nm)^3\right)\),就算秩只有 \(1\)(显然不可能),再 bitset
优化,复杂度 \(\mathrm O\!\left(\dfrac{(nm)^2}w\right)\) 那还是够呛。
那只能考虑逻辑优化,方法是减少变量和方程的数量。看哪些变量可以被其他变量表示,手动(而不是让高斯消元来)代入消元,既可减少变量也可减少方程。注意到一个点处列的方程涉及到的格子只会在 \(\pm2\) 行出现。那就考虑试图将 \(+2\) 行的变量用前 \(4\) 行的变量表示。从最上面开始的话其实只有 \(2\) 行,因为再往上就没有了。
对 \((1,1)\) 的方程,只有一个点是在 \(+2\) 行的。从 \((1,2)\) 开始,会有两个点在 \(+2\) 行,这就不好了。考虑令 \((3,1)\) 与上面两行有同等地位,这样就可以一路递推,将 \((3,2\sim m)\) 都用基本地位的格子表示出来。然后便可以接着往下递推了。这样一来,新变量只有前两行和第一列的并,是 \(\mathrm O(n)\) 级别的(假设 \(n=\mathrm O(m)\)),而其它旧变量都可以表示成新变量的线性组合(可能再平移上 \(1\),无妨)。这样表示只是满足条件的一个必要条件,因为最后两行和最后一列对应的方程还没得到检验。那就把这些方程列出来然后解哇,这样方程数和变量数都是 \(\mathrm O(n)\) 了,妥妥的。最后答案就是 \(2\) 的自由变量次方。
递推求线性表出式的时候,注意到这并不是严格意义上的线性组合,可能平移 \(1\),那就给常数项也分配一个位置,照样操作。\(n=1\) 的时候会出问题,但注意到 \(n,m\) 是对称的,如果不是 \(n=m=1\) 那就可以 swap
,否则直接输出 1
。
code
#include<bits/stdc++.h>
using namespace std;
#define mp make_pair
#define X first
#define Y second
#define pb push_back
const int mod=123456789,inf=0x3f3f3f3f;
const int N=160;
int n,m;
vector<bitset<3*N> > a;
bitset<3*N> xsm[N][N];
const int dx[]={-1,-1,-2,-2,1,1,2,2},dy[]={2,-2,1,-1,2,-2,1,-1};
bool ok(int x,int y){return 1<=x&&x<=n&&1<=y&&y<=m;}
void gauss(){
for(int i=0;;i++){
pair<int,int> p(inf,inf);
for(int j=i;j<a.size();j++)if(a[j].any()){
int x=a[j]._Find_first();
p=min(p,mp(x,j));
}
int col=p.X,row=p.Y;
if(row==inf)break;
swap(a[i],a[row]);
for(int j=0;j<a.size();j++)if(j!=i&&a[j][col])a[j]=a[j]^a[i];
}
}
struct KnightsOut{
int count(int _n,int _m){
n=_n,m=_m;
if(n==1&&m==1)return 1;
if(n==1)swap(n,m);
int now=0;
for(int i=1;i<=2;i++)for(int j=1;j<=m;j++)xsm[i][j].set(++now);
for(int i=3;i<=n;i++)xsm[i][1].set(++now);
for(int i=1;i<=n-2;i++)for(int j=1;j<=m-1;j++){
bitset<3*N> &nw=xsm[i+2][j+1];
nw.set(now+1),nw^=xsm[i][j];
for(int k=0;k<8;k++){
int x=i+dx[k],y=j+dy[k];
if(ok(x,y)&&!(x==i+2&&y==j+1))nw^=xsm[x][y];
}
}
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)if(i>n-2||j>m-1){
bitset<3*N> nw;
nw.set(now+1),nw^=xsm[i][j];
for(int k=0;k<8;k++){
int x=i+dx[k],y=j+dy[k];
if(ok(x,y))nw^=xsm[x][y];
}
a.pb(nw);
}
gauss();
int cnt=now,ans=1;
for(int i=0;i<a.size();i++)if(a[i].any()){
int x=a[i]._Find_first();
if(x==now+1)return 0;
cnt--;
}
while(cnt--)ans=2*ans%mod;
return ans;
}
};