可以發現如果我們只詢問一次答案其實就是詢問將其二分圖的鄰接矩陣建出來的行列式 $(\det A)$ 的奇偶性。
因爲 $\det(A)=\sum_{\sigma\in 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("==========\n");for(int i=1;i<=N;i++){for(int j=1;j<=2*N;j++) cout<<A[i][j]<<" ";printf("\n");}printf("===========\n");} 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\n":"YES\n"); return 0; }