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;
	}
};
posted @ 2021-08-17 10:45  ycx060617  阅读(83)  评论(0编辑  收藏  举报