【CF736D】Permutations 线性代数+高斯消元
【CF736D】Permutations
题意:有一个未知长度为n的排列和m个条件,第i个条件$(a_i,b_i)$表示第$a_i$个位置上的数可以为$b_i$。保证最终合法的排列的个数是奇数。现在有m个询问,第i个询问是问你在去掉第i个条件后,最终合法的排列数是奇数还是偶数。
$n\le 2000,m\le min(C_n^2,500000)$
题解:神题,滚去学线代了。
因为在$\mod 2$意义下,-1和1相等,所以方案数是什么?如果把所给条件看成一个01矩阵的话,则答案就是这个矩阵对应的行列式的值!而去掉一个条件(a,b)后的答案是什么?1xor行列式的代数余子式$M_{ab}$的值!而题目保证所给矩阵是可逆的,所以我们可以应用性质:
$A^{*}=|A|A^{-1}$(其中$A^{*}$表示伴随矩阵,$A_{ij}=M_{ji}$)
所以只需要求出原矩阵的逆即可,可以采用高斯消元。因为是$\mod 2$意义下的,所以可以采用bitset优化,时间复杂度$O({n^3\over 32})$。
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <bitset> using namespace std; int pa[500010],pb[500010]; bitset<4001> v[2005]; int n,m; inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<'0'||gc>'9') {if(gc=='-') f=-f; gc=getchar();} while(gc>='0'&&gc<='9') ret=ret*10+(gc^'0'),gc=getchar(); return ret*f; } int main() { n=rd(),m=rd(); int i,j; for(i=1;i<=m;i++) pa[i]=rd()-1,pb[i]=rd()-1,v[pa[i]][pb[i]]=1; for(i=0;i<n;i++) v[i][i+n]=1; for(i=0;i<n;i++) { if(!v[i][i]) { for(j=i+1;j<n;j++) if(v[j][i]) break; swap(v[j],v[i]); } for(j=0;j<n;j++) if(j!=i&&v[j][i]) v[j]^=v[i]; } for(i=1;i<=m;i++) { if(v[pb[i]][pa[i]+n]) puts("NO"); else puts("YES"); } return 0; }
| 欢迎来原网站坐坐! >原文链接<