可以发现如果我们只询问一次答案其实就是询问将其二分图的邻接矩阵建出来的行列式 $(det A)$ 的奇偶性。
因为 $det(A)=sum_{sigmain S_n} sgn(sigma)prod a_{i,sigma(i)}$ ,可以发现我们对于 $sigma$ 来说只有均为 $1$ 才做贡献,且由于奇偶性我们不需要知道 $sgn(sigma)$ 。
故我们只需要快速求将一个 $1$ 改成 $0$ 的行列式值为多少。
根据
$$
det(A)=sum_{c=1}^n a_{r,c}A_{r,c}
$$
其中 $A_{r,c}$ 表示 $a_{r,c}$ 的代数余子式,$A_{i,j}=(-1)^{i+j} M_{i,j}$ ,$M_{i,j}$ 表示 $A$ 中删去第 $i$ 行第 $j$ 列的行列式。
则我们将 $a_{r,c}=1$ 改成 $a_{r,c}=0$ 时我们只需要知道 $A_{r,c}$ 的奇偶性即可。
由于
$$
sum_{c=1}^n a_{i,c}A_{j,c}=det(A)cdot [i=j]
$$
将其按照矩阵乘法写出即为
$$
A(A_{i,j})^T=det(A)cdot I_n
$$
其中 $A^*=(A_{i,j})^T $,$A^*$ 表示 $A$ 的伴随矩阵。
其实我们将行/列展开的值是一样的,故 $AA^*=A^*A=det(A)cdot I_n$ 。
由于我们只需要快速知道 $A_{r,c}$ 即可,根据上式可写
$$
A^*=det(A)cdot A^{-1}
$$
又由于 $det(A)$ 为奇数,则我们只需要求 $A^{-1}$ 做个矩阵求逆就好啦!
求逆由于为 $0/1$ 异或故利用 $bitset$ 优化,时间复杂度 $O(dfrac{n^3}{w})$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #include<bitset> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=2e3+11; const int MAXM=5e5+11; bitset<MAXN*2> A[MAXN]; int N,M,U[MAXM],V[MAXM]; void print(){printf("========== ");for(int i=1;i<=N;i++){for(int j=1;j<=2*N;j++) cout<<A[i][j]<<" ";printf(" ");}printf("=========== ");} void Gauss(){ for(int i=1;i<=N;i++){ int ps=0; for(int j=i;j<=N;j++) if(A[j][i]){ps=j;break;} swap(A[i],A[ps]); for(int j=1;j<=N;j++){ if(!A[j][i]||i==j) continue; A[j]^=A[i]; } }return; } int main(){ //freopen("2.in","r",stdin); N=read(),M=read(); for(int i=1;i<=M;i++){int u=read(),v=read();U[i]=u,V[i]=v;A[u][v]=1;} for(int i=1;i<=N;i++) A[i][i+N]=1; Gauss(); for(int i=1;i<=M;i++) printf(A[V[i]][U[i]+N]?"NO ":"YES "); return 0; }