码迷,mamicode.com
首页 > 其他好文 > 详细

Codeforces 938G 线段树分治 线性基 可撤销并查集

时间:2019-08-13 15:54:31      阅读:75      评论:0      收藏:0      [点我收藏+]

标签:back   并查集   mem   之间   节点   hang   rollback   最小   插入   

Codeforces 938G Shortest Path Queries

一张连通图,三种操作

1.给x和y之间加上边权为d的边,保证不会产生重边

2.删除x和y之间的边,保证此边之前存在

3.询问x到y的路径异或最小值

保证图在任意时刻连通

首先连通图路径异或相当于从x到y的任意一条路径再异或上若干个环得到的,只要在dfs过程中把非树边成的环丢到线性基里就好了,其他环一定可以通过这些环异或组合出来

有加边删边操作怎么做呢?线段树时间分治!注意到不能保证在线段树的任意一个节点图是连通的,需要用可撤销并查集来维护,总复杂度\(O(q\log(n)\log(q))\)

tips:其实线性基因为一次插入只有一个赋值操作也可以用同样的方式撤销,不一定要每次都复制一遍下传

//segment tree divide and conquer
//linear base, dsu(rollback)
#include <bits/stdc++.h>
using namespace std;

const int N = 2e5 + 10;

struct Linear_Base {
    int a[30];
    Linear_Base() {memset(a, 0, sizeof(a));}
    void insert(int val) {
        for(int i = 29; ~i; --i) {
            if((val >> i) & 1) {
                if(a[i] == 0) {
                    a[i] = val;
                    return;
                }
                val ^= a[i];
            }
        }
    }
    int query(int val) {
        for(int i = 29; ~i; --i)
            val = min(val, val ^ a[i]);
        return val;
    }
};

pair<int*, int> save[N * 10];
int top;

void change(int &address, int val) {
    save[top++] = make_pair(&address, address);
    address = val;
}

void rollback(int st) {
    while(st != top) {
        top--;
        *save[top].first = save[top].second;
    }
}

struct edge {
    int x, y, d;
    edge() {x = y = d = 0;}
    edge(int _x, int _y, int _d) : x(_x), y (_y), d(_d) {}
};

struct que {
    int x, y, id;
    que() {x = y = id = 0;}
    que(int _x, int _y, int _id) : x(_x), y (_y), id(_id) {}
};

int ans[N], tot;

vector<edge> G[N << 2];
vector<que> Q[N << 2]; 

int dsu[N], size[N], dis[N];

int find(int x) {
    return x == dsu[x] ? x : find(dsu[x]);
}

int get_dist(int x) {
    return x == dsu[x] ? 0 : dis[x] ^ get_dist(dsu[x]); 
}

int unite(int x, int y, int d) {
    d ^= get_dist(x);
    d ^= get_dist(y);
    x = find(x);
    y = find(y);
    if(x == y) 
        return 0;
    if(size[x] < size[y]) swap(x, y);
    change(size[x], size[x] + size[y]);
    change(dsu[y], x);
    change(dis[y], d);
    return 1;
}

void update(int rt, int l, int r, int L, int R, const edge &x) {
    if(L <= l && r <= R) {G[rt].push_back(x); return;}
    int mid = l + r >> 1;
    if(L <= mid) update(rt << 1, l, mid, L, R, x);
    if(R > mid) update(rt << 1 | 1, mid + 1, r, L, R, x);
}

void add_query(int rt, int l, int r, int pos, const que &x) {
    if(l == r) {Q[rt].push_back(x); return;}
    int mid = l + r >> 1;
    if(pos <= mid) add_query(rt << 1, l, mid, pos, x);
    else add_query(rt << 1 | 1, mid + 1, r, pos, x);
}

void dfs(int rt, int l, int r, Linear_Base Base) {
    int st = top;
    for(auto e : G[rt]) {
        if(!unite(e.x, e.y, e.d)) {
            Base.insert(get_dist(e.x) ^ get_dist(e.y) ^ e.d);
        }
    }
    if(l == r) {
        for(auto q : Q[rt]) 
            ans[q.id] = Base.query(get_dist(q.x) ^ get_dist(q.y));
    }
    else {
        int mid = l + r >> 1;
        dfs(rt << 1, l, mid, Base);
        dfs(rt << 1 | 1, mid + 1, r, Base);
    }
    rollback(st);
}

int n, m, q, op, x, y, d;
map<pair<int, int>, pair<int, int>> st;

int main() {
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; ++i) dsu[i] = i, size[i] = 1, dis[i] = 0;
    for(int i = 1; i <= m; ++i) {
        scanf("%d%d%d", &x, &y, &d);
        st[make_pair(x, y)] = make_pair(1, d);
    }
    scanf("%d", &q);
    for(int i = 1; i <= q; ++i) {
        scanf("%d%d%d", &op, &x, &y);
        if(op == 1) {
            scanf("%d", &d);
            st[make_pair(x, y)] = make_pair(i, d);
        }
        else if(op == 2) {
            update(1, 1, q, st[make_pair(x, y)].first, i, edge(x, y, st[make_pair(x, y)].second));
            st.erase(make_pair(x, y));
        }
        else {
            add_query(1, 1, q, i, que(x, y, ++tot));
        }
    }
    for(auto e: st) {
        update(1, 1, q, e.second.first, q, edge(e.first.first, e.first.second, e.second.second));
    }
    dfs(1, 1, q, Linear_Base());
    for(int i = 1; i <= tot; ++i) {
        printf("%d\n", ans[i]);
    }
    return 0;
}

Codeforces 938G 线段树分治 线性基 可撤销并查集

标签:back   并查集   mem   之间   节点   hang   rollback   最小   插入   

原文地址:https://www.cnblogs.com/tusikalanse/p/11345944.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!