【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;
}
posted @ 2018-03-04 09:17  CQzhangyu  阅读(257)  评论(0编辑  收藏  举报