【集训队作业2018】围绕着我们的圆环
我貌似开始爆OJ了
主要是因为预处理的范围写小,以及第一次写带删除线性基,然后就调了好久/cy
如果把 \(A\) 看做一堆列向量,然后对于 \(C\) 的一个列向量 \(V\) ,以及对应列的 \(B\) 的列向量 \(B'\),由矩阵分块,可得以下式子:
即 \(V\) 是由 \(A\) 线性组合出的。并且 \(C\) 的每个列向量也在 \(A\) 构成的线性空间里。
固定 \(V\) 和 \(A\) ,可得 \(B\) 的数量有 \(2^{q - r}\),其中 \(r = rank(A)\)
易得,对于确定的 \(V\) 和 \(A\), \(B\) 的数量是 \(2^{s(q - r)}\)
现在需要求 \(A\) 的数量。
因为 \(A\) \(V\) 分别初等行变换 \(B\) 的解不变,所以对于秩相同的 \(V\) 都对应着相同的方案。
发现固定 \(V\) 求 \(r = r'\) 的 \(A\) 有多少比较麻烦。所以对每个固定的 \(A\) 求 \(V\) 的贡献,然后再除掉 \(V\) 的个数。
记 \(f_{n,m,r}\) 为 \(rank(A_{n,m}) = r\) 的方案数,显然对于一个固定的 \(n\),可以 \(O(n^2)\) DP 出来,转移只要讨论是已经被表示还是一个新的基。
枚举 \(r\) 易得 \(A\) 有 \(f_{p,q,r}\) 个,对于 \(C\) 只有 \(A\) 的基线性组合有效,所以考虑变换一下基,变为 \(A\) 的 \(r\) 个基,此时 \(C\) 的方案数不变,为 \(f_{r,s,x}\),其中 \(x = rank(C)\) (换基底就是乘上一个可逆矩阵)
此时答案很好表示
此时只要支持动态删除的线性基即可。
我们维护每个向量是被哪几个基异或出的。
考虑删除时要找个基来顶替,有两种情形:
- 存在一个非基被它异或过,那么直接异或不会降秩
- 不存在,则要找一个基,此时要找最小的那个,不然会发生比它小的基会有多个拥有相同的最高位。
实现时考虑到删除一个基的时候,那个基的位置会被异或上选择的那个基(根据异或性质)。
并且为了去掉要删除的基的贡献,同时计算新的基的贡献,发现那个要删除的会被贡献两次,即:
只要在被这个即将删除的基异或过的位置异或上我们用来顶替的那个基即可。
同时我在一开始写的时候写麻烦了,实际上不需要一个下标对应一个基,因为删除只和被哪些异或出有关。所以具体看代码。
#include <bits/stdc++.h>
const int MAXN = 1010;
const int mod = 1000000007;
typedef long long LL;
void reduce(int & x) { x += x >> 31 & mod; }
int mul(int a, int b) { return (LL) a * b % mod; }
int fastpow(int a, int b) {
int res = 1;
while (b) {
if (b & 1) res = mul(res, a);
a = mul(a, a);
b >>= 1;
}
return res;
}
typedef std::bitset<MAXN> B;
B A[MAXN], frm[MAXN];
int rnk, bse[MAXN], isb[MAXN];
int n, P, m, Q, typ;
void insert(int at, B x, int src) {
static B fx; fx.reset(); fx[src] = true;
isb[at] = 0;
for (int i = m; i; --i) if (x.test(i)) {
if (bse[i]) {
x ^= A[bse[i]];
fx ^= frm[bse[i]];
} else {
bse[i] = at;
isb[at] = i;
++rnk;
break;
}
}
A[at] = x, frm[at] = fx;
}
int remove(int at) {
int ax = 0; isb[0] = 1001;
for (int i = 1; i <= n; ++i)
if (frm[i].test(at))
if (isb[i] < isb[ax]) ax = i;
rnk -= (bool) isb[ax];
for (int i = 1; i <= n; ++i)
if (i != ax && frm[i].test(at))
frm[i] ^= frm[ax], A[i] ^= A[ax];
if (isb[ax]) bse[isb[ax]] = 0;
return ax;
}
int f[MAXN][MAXN], g[MAXN][MAXN], ansl[MAXN], pow2[MAXN * MAXN];
void predo() {
pow2[0] = f[0][0] = g[0][0] = 1;
int ran = std::max(n, m * P);
for (int i = 1; i <= ran; ++i)
reduce(pow2[i] = pow2[i - 1] * 2 - mod);
ran = std::max(std::max(n, m), P);
for (int i = 0; i < ran; ++i)
for (int j = 0; j <= i; ++j) {
reduce(f[i + 1][j] += mul(f[i][j], pow2[j]) - mod);
reduce(f[i + 1][j + 1] += mul(f[i][j], pow2[n] - pow2[j] + mod) - mod);
reduce(g[i + 1][j] += mul(g[i][j], pow2[j]) - mod);
reduce(g[i + 1][j + 1] += mul(g[i][j], pow2[m] - pow2[j] + mod) - mod);
}
ran = std::min(n, P);
for (int i = std::min(n, m); ~i; --i) {
int & ans = ansl[i] = 0;
for (int x = i; x <= ran; ++x)
reduce(ans += (LL) f[P][x] * g[x][i] % mod * pow2[m * (P - x)] % mod - mod);
ans = mul(ans, fastpow(f[m][i], mod - 2));
}
}
int main() {
std::ios_base::sync_with_stdio(false), std::cin.tie(0);
std::cin >> n >> P >> m >> Q >> typ;
predo();
B tx;
for (int i = 1; i <= n; ++i) {
tx.reset();
for (int j = 1, t; j <= m; ++j)
std::cin >> t, tx[j] = t;
insert(i, tx, i);
}
std::cout << ansl[rnk] << '\n';
while (Q --> 0) {
int at, t;
std::cin >> at; at ^= typ * ansl[rnk];
tx.reset();
for (int j = 1; j <= m; ++j)
std::cin >> t, tx[j] = t;
insert(remove(at), tx, at);
std::cout << ansl[rnk] << '\n';
}
return 0;
}