首先不知道有没有神仙线段树分治过的。

首先一个较为显然的性质:

\[ \mathrm{Span}\{v_1, v_2, \dots, v_n\} = \mathrm{Span}\{v_1, v_2 - v_1, \dots, v_n - v_{n - 1}\} \]

这个启发我们维护差分序列,此时1操作变成了单点异或,2操作变成了单点异或以及区间清空。

但是这道题维护的是线性基,所以要用带删除的线性基实现。(具体见【集训队作业2018】围绕着我们的圆环)

此时单点异或显然可以变成插入再删除。因为线性基的时间复杂度,此时暴力清空也没问题。

因为每次操作最多使两个位置非负,暴力清空时特判空向量,此时可以摊还分析,复杂度正确。

因此复杂度为 \(O(\frac{(n+m)mQ}{\omega})\)

这次优化了带删除线性基的代码(从虞大那里参考了部分写法)

#include <bits/stdc++.h>

const int MAXN = 2048;
typedef std::bitset<MAXN> B;
int n, m, Q;
B frm[MAXN], A[MAXN], arr[MAXN];
int bse[MAXN], isb[MAXN];
B read() {
    static B t; t.reset();
    static char buf[MAXN]; std::cin >> buf;
    for (int i = 1; i <= m; ++i) if (buf[i - 1] & 1) t.set(m - i + 1);
    return t;
}
void insert(int at) {
    for (int i = m; i; --i) if (A[at].test(i)) {
        if (bse[i]) A[at] ^= A[bse[i]], frm[at] ^= frm[bse[i]];
        else { isb[bse[i] = at] = i; break; }
    }
}
void modify(int at, B v) {
    if (v.none()) return ;
    int ax = 0; isb[0] = 2001;
    for (int i = 1; i <= n; ++i)
        if (frm[i].test(at))
            isb[i] < isb[ax] ? ax = i : 0;
    for (int i = 1; i <= n; ++i)
        if (i != ax && frm[i].test(at))
            A[i] ^= A[ax], frm[i] ^= frm[ax];
    if (isb[ax]) bse[isb[ax]] = 0, isb[ax] = 0;
    A[ax] ^= v, insert(ax);
}
void output(const B & x) {
    static char buf[MAXN];
    for (int i = 1; i <= m; ++i) buf[i - 1] = x[m - i + 1] + '0';
    std::cout << buf << std::endl;
}
B query() {
    static B t; t.reset();
    for (int i = m; i; --i)
        if (!t.test(i) && bse[i])
            t ^= A[bse[i]];
    return t;
}
int main() {
    std::ios_base::sync_with_stdio(false), std::cin.tie(0);
    std::cin >> n >> m >> Q;
    B t;
    for (int i = 1; i <= n; ++i)
        A[i] = (arr[i] = read()) ^ arr[i - 1], frm[i].set(i), insert(i);
    while (Q --> 0) {
        int opt, t1, t2;
        std::cin >> opt;
        if (opt == 1) {
            std::cin >> t1 >> t2; t = read();
            modify(t1, t);
            if (t2 < n) modify(t2 + 1, t);
            for (int i = t1; i <= t2; ++i) arr[i] ^= t;
        } else if (opt == 2) {
            std::cin >> t1 >> t2; t = read();
            modify(t1, arr[t1] ^ t);
            if (t2 < n) modify(t2 + 1, arr[t2] ^ t);
            for (int i = t1 + 1; i <= t2; ++i)
                modify(i, arr[i] ^ arr[i - 1]);
            for (int i = t1; i <= t2; ++i) arr[i] = t;
        } else if (opt == 3) output(query());
    }
    return 0;
}
01-19 03:48