[HAOI2017] 八纵八横

给定 (n) 个点 (m) 条边的带权无向图,权值是小于 (2^{1000}) 的二进制数。有 (q) 次操作,每次操作可能会新增一条边,删除一条边或者修改某条边的权值。你需要在每次操作后,输出图中的最大权环,环的权值定义为环上所有边的异或和,环可以多次经过某个点或某条边。(n,m leq 500, q leq 1000)

Solution

一个很显然的 70 分暴力是直接按照 [WC2011] XOR 那题的方法做,然而线性基不支持撤销,我们要向将冗余的部分优化掉,需要另寻思路

考虑到每条边的存在时间是一个区间,容易想到线段树分治

首先搞出原图的一棵生成树,这样我们就可以让多出来的每条边对应唯一的一个环,于是每条边所对应的环的权值也就随之确定

这样问题就转化为,一开始集合中有 (m-n+1) 个数,现在我们要操作 (q) 次,每次新增一个数,删除一个数,或者修改一个数,不妨把修改一个数视为删除后再添加,那么就只有添加和删除两种操作

于是我们处理出每个数存在的时间区间,将它挂到线段树上,然后跑线段树分治

具体地,每递归到一个节点时,拷贝一份父亲节点的线性基,并在其中插入这个节点上挂的数字,就可以得到这个点的线性基

递归到叶子节点的时候,求一下线性基中的最大异或和即可

注意有些新增的边不会被删除,所以最后要把它们处理一下

#include <bits/stdc++.h>
using namespace std;

const int dbg = 0;
const int N = 1005;
const int W = 1000;

int n, m, q, ww;

struct pib {
    int q;
    bitset<W> w;
};
struct edge {
    int u, v;
    bitset<W> w;
} ed[N * 2];
vector<pib> g[N];

void print(bitset<W> b) {
    for (int j = ww - 1; j >= 0; --j) cout << b[j];
}

namespace tree {
int vis[N], ontree[N / 2][N / 2], used[N / 2][N / 2];
bitset<W> dist[N / 2][N / 2];
void dfs(int p) {
    vis[p] = 1;
    for (pib it : g[p]) {
        int q = it.q;
        if (vis[q] == 0) {
            ontree[p][q] = ontree[q][p] = 1;
            dfs(q);
        }
    }
}
void calc(int p, bitset<W> *d) {
    vis[p] = 1;
    for (pib it : g[p]) {
        int q = it.q;
        if (vis[q] == 0) {
            d[q] = d[p] ^ it.w;
            calc(q, d);
        }
    }
}
void solve() {
    dfs(1);
    for (int i = 1; i <= n; i++) g[i].clear();
    for (int i = 1; i <= m; i++) {
        if (ontree[ed[i].u][ed[i].v]) {
            if (used[ed[i].u][ed[i].v] == 0) {
                used[ed[i].u][ed[i].v] = used[ed[i].v][ed[i].u] = 1;
                g[ed[i].u].push_back({ ed[i].v, ed[i].w });
                g[ed[i].v].push_back({ ed[i].u, ed[i].w });
            }
        }
    }
    for (int i = 1; i <= n; i++) {
        memset(vis, 0, sizeof vis);
        calc(i, dist[i]);
    }
}
}  // namespace tree

struct linearbase {
    bitset<W> a[W + 1];
    void clear() {
        for (int i = 0; i <= W; i++) a[i] = 0;
    }
    void insert(bitset<W> k) {
        for (int j = W - 1; j >= 0; --j)
            if ((k >> j)[0])
                if (a[j] == 0) {
                    a[j] = k;
                    break;
                } else
                    k ^= a[j];
    }
    bitset<W> solve() {
        bitset<W> ans;
        for (int i = W - 1; i >= 0; --i)
            if (a[i].count() && ans[i] == 0)
                ans ^= a[i];
        return ans;
    }
};

char str[N];
int t1, t2, t3;

struct railway {
    int t, u, v;
    bitset<W> w;
} rail[N];

namespace seg {
vector<bitset<W> > a[N * 4];
int dep = 0;
bitset<W> ans[N * 4];
void modify(int p, int l, int r, int ql, int qr, bitset<W> v) {
    if (l > qr || r < ql)
        return;
    if (l >= ql && r <= qr) {
        a[p].push_back(v);
    } else {
        modify(p * 2, l, (l + r) / 2, ql, qr, v);
        modify(p * 2 + 1, (l + r) / 2 + 1, r, ql, qr, v);
    }
}
void modify(int ql, int qr, bitset<W> v) {
    if (dbg) {
        cout << "seg::modify ql=" << ql << " qr=" << qr << " v=";
        print(v);
        cout << endl;
    }
    ++ql;
    ++qr;
    modify(1, 1, q + 1, ql, qr, v);
}
void solve(int p, int l, int r, linearbase b) {
    ++dep;
    for (bitset<W> i : a[p]) b.insert(i);
    if (l == r) {
        ans[l] = b.solve();
    } else {
        solve(p * 2, l, (l + r) / 2, b);
        solve(p * 2 + 1, (l + r) / 2 + 1, r, b);
    }
    --dep;
}
void solve() {
    linearbase lb;
    solve(1, 1, q + 1, lb);
}
}  // namespace seg

signed main() {
    ios::sync_with_stdio(false);
    cin >> n >> m >> q;
    for (int i = 1; i <= m; i++) {
        cin >> t1 >> t2 >> str;
        int len = strlen(str);
        ww = max(ww, len);
        for (int j = 0; j < len; j++) ed[i].w[j] = str[len - j - 1] - '0';
        ed[i].u = t1;
        ed[i].v = t2;
        g[t1].push_back({ t2, ed[i].w });
        g[t2].push_back({ t1, ed[i].w });
    }
    tree::solve();
    using seg::modify;
    using tree::dist;
    using tree::ontree;
    for (int i = 1; i <= m; i++) {
        if (!ontree[ed[i].u][ed[i].v]) {
            modify(0, q, dist[ed[i].u][ed[i].v] ^ ed[i].w);
        } else {
            ontree[ed[i].u][ed[i].v] = ontree[ed[i].v][ed[i].u] = 0;
        }
    }
    int ind = 0;
    for (int i = 1; i <= q; i++) {
        cin >> str;
        if (str[1] == 'd') {
            ++ind;
            rail[ind].t = i;
            cin >> rail[ind].u >> rail[ind].v >> str;
            int len = strlen(str);
            ww = max(ww, len);
            for (int j = 0; j < len; j++) rail[ind].w[j] = str[len - j - 1] - '0';
        } else if (str[1] == 'h') {
            cin >> t1;
            modify(rail[t1].t, i - 1, rail[t1].w ^ dist[rail[t1].u][rail[t1].v]);
            cin >> str;
            int len = strlen(str);
            ww = max(ww, len);
            rail[t1].w = 0;  //!
            for (int j = 0; j < len; j++) rail[t1].w[j] = str[len - j - 1] - '0';
            rail[t1].t = i;
        } else if (str[1] == 'a') {
            cin >> t1;
            if (dbg) {
                cout << "dbg Cancel ";
                print(rail[t1].w);
                cout << " ";
                print(dist[rail[t1].u][rail[t1].v]);
                cout << endl;
            }
            modify(rail[t1].t, i - 1, rail[t1].w ^ dist[rail[t1].u][rail[t1].v]);
            rail[t1].t = 0;
        }
    }
    for (int i = 1; i <= ind; i++)
        if (rail[i].t) {
            modify(rail[i].t, q, rail[i].w ^ dist[rail[i].u][rail[i].v]);
        }
    seg::solve();
    using seg::ans;
    for (int i = 1; i <= q + 1; i++) {
        print(ans[i]);
        cout << endl;
    }
}

原文地址:https://www.cnblogs.com/mollnn/p/12410658.html