JZOJ 5553 谜(线性基,矩阵的秩)

Description:

在这里插入图片描述
在这里插入图片描述

题解:

考虑如何在多项式复杂度内求完美匹配的方案数mod 2

完美匹配相当于求所有排列p的 ∏ a [ i ] [ p [ i ] ] \prod a[i][p[i]] a[i][p[i]]

这个东西和行列式长得非常像啊:
∣ A ∣ = ∑ 排 列 p ( − 1 ) p 的 逆 序 对 数 ∗ ∏ A [ i ] [ p [ i ] ] |A|=\sum_{排列p}{(-1)}^{p的逆序对数}*\prod A[i][p[i]] A=p(1)pA[i][p[i]]

( − 1 ) (-1) (1)在mod 2意义下就是1,所以 方 案 数   m o d   2 = ∣ A ∣   m o d   2 方案数~mod~2=|A|~mod~2  mod 2=A mod 2

直接高斯消元可以获得40分。

∣ A ∣ = 1 ( m o d   2 ) |A|=1(mod~2) A=1(mod 2)也就是A满秩。

考虑把一行替换后依然满秩的条件,首先替换前的秩必须=n或n-1,因为替换一行秩最多+1。

替换前=n
那么新的这一行一定能被前面的表示,显然的是,那么表示这一行的行中包含要替换的那一行。

替换前=n-1
首先新的这一行不能被表示,并且替换的那行是删掉后秩不变的行,我们设这种行为多余的行。

考虑怎么求多余的行,在构建线性基的过程中,有一行不能被加进去,这一行肯定是,并且表示这一行的行也是。

那么预处理是 O ( n 3 / Ω ) O(n^3/Ω) O(n3/Ω)的,查询 O ( 1 ) O(1) O(1)

Code:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define ff(i, x, y) for(int i = x, B = y; i <  B; i ++)
#define fd(i, x, y) for(int i = x, B = y; i >= B; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

#define gc getchar
int get() {
	char c = ' ';
	while(c != '0' && c != '1') c = gc();
	return c - '0';
}
const int N = 1005;

int n, m, k;
typedef bitset<N> jz;
jz a[N];

struct nod {
	jz z[N], a[N], b[N], c, d, f;
	int zhi, fail;
	jz e[N], w[N];
	int ky[N];
	void build(int t) {
		fo(i, 1, n) {
			int Z = zhi;
			c.reset(); c[i] = 1;
			fd(j, n, 1) if(z[i][j]) {
				if(a[j].count()) {
					z[i] ^= a[j];
					c ^= b[j];
				} else {
					zhi ++;
					a[j] = z[i]; b[j] = c;
					fo(k, 1, j - 1) if(a[j][k] && a[k].count())
						a[j] ^= a[k], b[j] ^= b[k];
					fo(k, j + 1, n) if(a[k][j])
						a[k] ^= a[j], b[k] ^= b[j];
					break;
				}
			}
			if(Z == zhi) d = c;
		}
		if(zhi == n - 1) f = d;
		fo(i, 1, t) {
			if(zhi == n) {
				fo(j, 1, n)	if(e[i][j]) w[i] ^= b[j];
			} else
			if(zhi == n - 1) {
				fd(j, n, 1) if(e[i][j])
					e[i] ^= a[j];
				ky[i] = !e[i].count();
			}
		}
	}
	int query(int x, int y) {
		if(zhi < n - 1) return 0;
		if(zhi == n) return w[x][y];
		if(zhi == n - 1) return !ky[x] && f[y];
	}
} s1, s2;

int q, ty, x, y;

int main() {
	freopen("maze.in", "r", stdin);
	freopen("maze.out", "w", stdout);
	scanf("%d", &n);
	fo(i, 1, n) fo(j, 1, n) a[i][j] = get();
	fo(i, 1, n) fo(j, 1, n) s1.z[i][j] = a[i][j], s2.z[j][i] = a[i][j];
	scanf("%d %d", &m, &k);
	fo(i, 1, m) fo(j, 1, n) s1.e[i][j] = get();
	fo(i, 1, k) fo(j, 1, n) s2.e[i][j] = get();
	s1.build(m); s2.build(k);
	pp("%d\n", s1.zhi == n);
	scanf("%d", &q);
	fo(ii, 1, q) {
		scanf("%d %d %d", &ty, &x, &y);
		if(ty == 0) {
			pp("%d\n", s1.query(y, x));
		} else {
			pp("%d\n", s2.query(y, x));
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值