【学习笔记】可持久化并查集

1.算法

用主席树维护 f a fa fa d e p t h depth depth 数组,其他的和并查集只用按秩合并一模一样。

2.模板

#define ls (Tr[p].ch[0])
#define rs (Tr[p].ch[1])
#define fap (Tr[p].fa)
#define depthp (Tr[p].depth)
#define valp (Tr[p].val)
struct Node {
    int fa, depth, ch[2]; int val;
};
struct Persistent_Tree {
    int pool;
    Node Tr[Maxtree + 5];

    Persistent_Tree () { pool = 0; }
    int New_Node (int rt) {
        int p = ++pool;
        ls = Tr[rt].ch[0];
        rs = Tr[rt].ch[1];
        fap = Tr[rt].fa;
        depthp = Tr[rt].depth;
        valp = Tr[rt].val;
        return p;
    }
    int Build (int rt, int l, int r) {
        int p = New_Node (rt);
        if (l == r) {
            fap = l; depthp = 1; valp = val[l];
            return p;
        }
        int mid = (l + r) >> 1;
        ls = Build (Tr[rt].ch[0], l, mid);
        rs = Build (Tr[rt].ch[1], mid + 1, r);
        return p;
    }
    int Update (int rt, int l, int r, int Index, int x, int op) {
        int p = New_Node (rt);
        if (l == r) {
            if (op == 0) fap = x;
            else if (op == 1) depthp = x;
            else valp = x;
            return p;
        }
        int mid = (l + r) >> 1;
        if (Index <= mid) ls = Update (Tr[rt].ch[0], l, mid, Index, x, op);
        else rs = Update (Tr[rt].ch[1], mid + 1, r, Index, x, op);
        return p;
    }
    int Query (int p, int l, int r, int Index) {
        if (l == r)
            return p;
        int mid = (l + r) >> 1;
        if (Index <= mid) return Query (ls, l, mid, Index);
        else return Query (rs, mid + 1, r, Index);
    }
};
struct Persistent_Disjoint_Set_Union {
    int n, timestamp, Fuck;
    int Root[Maxtree + 5];
    Persistent_Tree Tree;

    void Init (int x) {
        n = x; timestamp = 0; Fuck = 0; Tree.pool = 0;
        Root[timestamp] = Tree.Build (0, 1, n);
    }
    int FindSet (int x) {
        int p = Tree.Query (Root[timestamp], 1, n, x);
        if (Tree.Tr[p].fa == x) return x;
        else return FindSet (Tree.Tr[p].fa);
    }
    int Query_Depth (int x) {
        int p = Tree.Query (Root[timestamp], 1, n, x);
        return Tree.Tr[p].depth;
    }
    int Update (int Now, int Index, int x, int op) {
        return Tree.Update (Root[Now], 1, n, Index, x, op);
    }
    int Query_Val (int Now, int Index) {
        int p = Tree.Query (Root[Now], 1, n, Index);
        return Tree.Tr[p].val;
    }
    void Make_Fa (int x, int y) {
        int dep = Max (Query_Depth (x) + 1, Query_Depth (y));
        ++Fuck; Root[Fuck] = Update (timestamp, x, y, 0); timestamp = Fuck;
        ++Fuck; Root[Fuck] = Update (timestamp, y, dep, 1); timestamp = Fuck;
        ++Fuck; Root[Fuck] = Update (timestamp, y, Min (Query_Val (timestamp, x), Query_Val (timestamp, y)), 2); timestamp = Fuck;
    }
    void UnionSet (int x, int y) {
        int u = FindSet (x), v = FindSet (y);
        if (u == v) return;
        if (Query_Depth (u) < Query_Depth (v)) Make_Fa (u, v);
        else Make_Fa (v, u);
    }
    bool Same_Set (int x, int y) {
        int u = FindSet (x), v = FindSet (y);
        if (u == v) return 1;
        else return 0;
    }
}DSU;
一、可持久化并查集

板子,只是注意回到 k k k 次操作指的是操作序列中的第 k k k 次操作,而且只是把 T a Ta Ta 的所有信息全部 c o p y copy copy 过来 (我因为这个调了一个下午, 吃柠檬)。

//author : LH ????Who just can eat S??t
//worship WJC ????Who can f??k tourist up and down and loves ?????
//worship YJX ????Who can f??k WJC up and down
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstdlib> 
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define fi first
#define se second
#define db double
//#define int long long
#define LL long long
#define ULL unsigned long long
#define PII pair <int, int>
#define MP(x,y) make_pair (x, y)
#define rep(i,j,k) for (int i = (j); i <= (k); ++i)
#define per(i,j,k) for (int i = (j); i >= (k); --i)

template <typename T> T Max (T x, T y) { return x > y ? x : y; }
template <typename T> T Min (T x, T y) { return x < y ? x : y; }
template <typename T> T Abs (T x) { return x > 0 ? x : -x; }
template <typename T>
void read (T &x) {
    x = 0; T f = 1;
    char ch = getchar ();
    while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar ();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 3) + (x << 1) + ch - '0';
        ch = getchar ();
    }
    x *= f;
}
template <typename T, typename... Args>
void read (T &x, Args&... args) {
    read (x); read (args...);
}
char For_Print[25];
template <typename T>
void write (T x) {
    if (x == 0) { putchar ('0'); return; }
    if (x < 0) { putchar ('-'); x = -x; }
    int poi = 0;
    while (x) {
        For_Print[++poi] = x % 10 + '0';
        x /= 10;
    }
    while (poi) putchar (For_Print[poi--]);
}
template <typename T>
void print (T x, char ch) {
    write (x); putchar (ch);
}

const LL Mod = 1e9 + 7;

LL square (LL x) { return (x * x) % Mod; }
void DEL (LL &x, LL y) { ((x -= y) < 0) && (x += Mod); }
void ADD (LL &x, LL y) { ((x += y) >= Mod) && (x -= Mod); }

const int Maxtree = 5 * 1e7;

#define ls (Tr[p].ch[0])
#define rs (Tr[p].ch[1])
#define fap (Tr[p].fa)
#define depthp (Tr[p].depth)
struct Node {
    int fa, depth, ch[2];
};
struct Persistent_Tree {
    int pool;
    Node Tr[Maxtree + 5];

    Persistent_Tree () { pool = 0; }
    int New_Node (int rt) {
        int p = ++pool;
        ls = Tr[rt].ch[0];
        rs = Tr[rt].ch[1];
        fap = Tr[rt].fa;
        depthp = Tr[rt].depth;
        return p;
    }
    int Build (int rt, int l, int r) {
        int p = New_Node (rt);
        if (l == r) {
            fap = l; depthp = 1;
            return p;
        }
        int mid = (l + r) >> 1;
        ls = Build (Tr[rt].ch[0], l, mid);
        rs = Build (Tr[rt].ch[1], mid + 1, r);
        return p;
    }
    int Update (int rt, int l, int r, int Index, int x, int op) {
        int p = New_Node (rt);
        if (l == r) {
            if (!op) fap = x;
            else depthp = x;
            return p;
        }
        int mid = (l + r) >> 1;
        if (Index <= mid) ls = Update (Tr[rt].ch[0], l, mid, Index, x, op);
        else rs = Update (Tr[rt].ch[1], mid + 1, r, Index, x, op);
        return p;
    }
    int Query (int p, int l, int r, int Index) {
        if (l == r)
            return p;
        int mid = (l + r) >> 1;
        if (Index <= mid) return Query (ls, l, mid, Index);
        else return Query (rs, mid + 1, r, Index);
    }
};
struct Persistent_Disjoint_Set_Union {
    int n, timestamp, Fuck;
    int Root[Maxtree + 5];
    Persistent_Tree Tree;

    void Init (int x) {
        n = x; timestamp = 0; Fuck = 0;
        Root[timestamp] = Tree.Build (0, 1, n);
    }
    int FindSet (int x) {
        int p = Tree.Query (Root[timestamp], 1, n, x);
        if (Tree.Tr[p].fa == x) return x;
        else return FindSet (Tree.Tr[p].fa);
    }
    int Query_Depth (int x) {
        int p = Tree.Query (Root[timestamp], 1, n, x);
        return Tree.Tr[p].depth;
    }
    int Update (int Now, int Index, int x, int op) {
        return Tree.Update (Root[Now], 1, n, Index, x, op);
    }
    void Make_Fa (int x, int y) {
        int dep = Max (Query_Depth (x) + 1, Query_Depth (y));
        ++Fuck; Root[Fuck] = Update (timestamp, x, y, 0); timestamp = Fuck;
        ++Fuck; Root[Fuck] = Update (timestamp, y, dep, 1); timestamp = Fuck;
    }
    void UnionSet (int x, int y) {
        int u = FindSet (x), v = FindSet (y);
        if (u == v) return;
        if (Query_Depth (u) < Query_Depth (v)) Make_Fa (u, v);
        else Make_Fa (v, u);
    }
    bool Same_Set (int x, int y) {
        int u = FindSet (x), v = FindSet (y);
        if (u == v) return 1;
        else return 0;
    }
}DSU;

int n, m;
int timestamp[Maxtree + 5];

signed main () {
	// freopen ("D:\\lihan\\1.in", "r", stdin);
	// freopen ("D:\\lihan\\1.out", "w", stdout);

    read (n, m);
    DSU.Init (n); timestamp[0] = DSU.timestamp;
    rep (i, 1, m) {

        // printf ("step = %d\n", i);
        // rep (j, 1, n) {
        //     printf ("   fa[%d] = %d\n", j, DSU.FindSet (j));
        // }

        int op, x, y; read (op);
        if (op == 1) {
            read (x, y);
            DSU.UnionSet (x, y);
        }
        else if (op == 2) {
            read (x);
            DSU.timestamp = timestamp[x];
        }
        else {
            read (x, y);
            print (DSU.Same_Set (x, y), '\n');
        }
        timestamp[i] = DSU.timestamp;
    }
    return 0;
}
二、归程

这道题的思路基于一个简单的想法:如果一条边 ( x , y ) (x, y) (x,y) 没有积水,那么给这两个包含 x , y x, y x,y 的连通块之间连一条边,新联通块的所有的点,走到终点的代价都是联通块中到达终点最快的点的代价(即 x , y x, y x,y 连通块代价的最小值)。

那么暴力就是枚举所有的边,高于水线就加入,否则就不加。

很快就能想到优化,按照水线从大到小排序,那么要加的边一定是类似于 [ 1 , p ] [1, p] [1,p] 这样的左端点为 1 1 1 的区间,那么从一开始插入,询问就是第 p p p 个版本的并查集。

大常数人去死

//author : LH ????Who just can eat S??t
//worship WJC ????Who can f??k tourist up and down and loves ?????
//worship YJX ????Who can f??k WJC up and down
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstdlib> 
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define fi first
#define se second
#define db double
#define ULL unsigned long long
#define PII pair <int, int>
#define MP(x,y) make_pair (x, y)
#define rep(i,j,k) for (int i = (j); i <= (k); ++i)
#define per(i,j,k) for (int i = (j); i >= (k); --i)

template <typename T> T Max (T x, T y) { return x > y ? x : y; }
template <typename T> T Min (T x, T y) { return x < y ? x : y; }
template <typename T> T Abs (T x) { return x > 0 ? x : -x; }
template <typename T>
void read (T &x) {
    x = 0; T f = 1;
    char ch = getchar ();
    while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar ();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 3) + (x << 1) + ch - '0';
        ch = getchar ();
    }
    x *= f;
}
template <typename T, typename... Args>
void read (T &x, Args&... args) {
    read (x); read (args...);
}
char For_Print[25];
template <typename T>
void write (T x) {
    if (x == 0) { putchar ('0'); return; }
    if (x < 0) { putchar ('-'); x = -x; }
    int poi = 0;
    while (x) {
        For_Print[++poi] = x % 10 + '0';
        x /= 10;
    }
    while (poi) putchar (For_Print[poi--]);
}
template <typename T>
void print (T x, char ch) {
    write (x); putchar (ch);
}

const int Maxtree = 5 * 1e7;
const int Maxn = 5 * 1e5;

int t, n, m;

struct edge {
    int x, y, l, h;
}e[Maxn + 5];
bool cmp (edge x, edge y) {
    return x.h > y.h;
}
vector <edge> g[Maxn + 5];
void add (int x, int y, int l, int h) {
    g[x].push_back ({0, y, l, h});
}

int dist[Maxn + 5];
bool vis[Maxn + 5];
void Dijkstra () {
    memset (vis, 0, sizeof vis);
    memset (dist, 0x3f, sizeof dist); dist[1] = 0;
    priority_queue <PII, vector <PII>, greater <PII> > p; p.push (MP (0, 1));
    while (p.size ()) {
        PII tmp = p.top (); p.pop ();
        int u = tmp.se;
        if (vis[u]) continue; vis[u] = 1;
        for (auto i : g[u]) {
            int v = i.y, l = i.l;
            if (vis[v]) continue;
            if (dist[v] > dist[u] + l) {
                dist[v] = dist[u] + l;
                p.push (MP (dist[v], v));
            }
        }
    }
}

#define ls (Tr[p].ch[0])
#define rs (Tr[p].ch[1])
#define fap (Tr[p].fa)
#define depthp (Tr[p].depth)
#define distp (Tr[p].dist)
struct Node {
    int fa, depth, ch[2]; int dist;
};
struct Persistent_Tree {
    int pool;
    Node Tr[Maxtree + 5];

    Persistent_Tree () { pool = 0; }
    int New_Node (int rt) {
        int p = ++pool;
        ls = Tr[rt].ch[0];
        rs = Tr[rt].ch[1];
        fap = Tr[rt].fa;
        depthp = Tr[rt].depth;
        distp = Tr[rt].dist;
        return p;
    }
    int Build (int rt, int l, int r) {
        int p = New_Node (rt);
        if (l == r) {
            fap = l; depthp = 1; distp = dist[l];
            return p;
        }
        int mid = (l + r) >> 1;
        ls = Build (Tr[rt].ch[0], l, mid);
        rs = Build (Tr[rt].ch[1], mid + 1, r);
        return p;
    }
    int Update (int rt, int l, int r, int Index, int x, int op) {
        int p = New_Node (rt);
        if (l == r) {
            if (op == 0) fap = x;
            else if (op == 1) depthp = x;
            else distp = x;
            return p;
        }
        int mid = (l + r) >> 1;
        if (Index <= mid) ls = Update (Tr[rt].ch[0], l, mid, Index, x, op);
        else rs = Update (Tr[rt].ch[1], mid + 1, r, Index, x, op);
        return p;
    }
    int Query (int p, int l, int r, int Index) {
        if (l == r)
            return p;
        int mid = (l + r) >> 1;
        if (Index <= mid) return Query (ls, l, mid, Index);
        else return Query (rs, mid + 1, r, Index);
    }
};
struct Persistent_Disjoint_Set_Union {
    int n, timestamp, Fuck;
    int Root[Maxtree + 5];
    Persistent_Tree Tree;

    void Init (int x) {
        n = x; timestamp = 0; Fuck = 0; Tree.pool = 0;
        Root[timestamp] = Tree.Build (0, 1, n);
    }
    int FindSet (int x) {
        int p = Tree.Query (Root[timestamp], 1, n, x);
        if (Tree.Tr[p].fa == x) return x;
        else return FindSet (Tree.Tr[p].fa);
    }
    int Query_Depth (int x) {
        int p = Tree.Query (Root[timestamp], 1, n, x);
        return Tree.Tr[p].depth;
    }
    int Update (int Now, int Index, int x, int op) {
        return Tree.Update (Root[Now], 1, n, Index, x, op);
    }
    int Query_Dist (int Now, int Index) {
        int p = Tree.Query (Root[Now], 1, n, Index);
        return Tree.Tr[p].dist;
    }
    void Make_Fa (int x, int y) {
        int dep = Max (Query_Depth (x) + 1, Query_Depth (y));
        ++Fuck; Root[Fuck] = Update (timestamp, x, y, 0); timestamp = Fuck;
        ++Fuck; Root[Fuck] = Update (timestamp, y, dep, 1); timestamp = Fuck;
        ++Fuck; Root[Fuck] = Update (timestamp, y, Min (Query_Dist (timestamp, x), Query_Dist (timestamp, y)), 2); timestamp = Fuck;
    }
    void UnionSet (int x, int y) {
        int u = FindSet (x), v = FindSet (y);
        if (u == v) return;
        if (Query_Depth (u) < Query_Depth (v)) Make_Fa (u, v);
        else Make_Fa (v, u);
    }
    bool Same_Set (int x, int y) {
        int u = FindSet (x), v = FindSet (y);
        if (u == v) return 1;
        else return 0;
    }
}DSU;

int q, k, s;
int timestamp[Maxn + 5];

signed main () {
	freopen ("return.in", "r", stdin);
	freopen ("return.out", "w", stdout);

    read (t);
    while (t--) {
        read (n, m);

        rep (i, 1, n) g[i].clear ();

        DSU.Init (n);
        rep (i, 1, m) {
            read (e[i].x, e[i].y, e[i].l, e[i].h);
            add (e[i].x, e[i].y, e[i].l, e[i].h); add (e[i].y, e[i].x, e[i].l, e[i].h);
        }
        sort (e + 1, e + 1 + m, cmp);
        Dijkstra ();
        DSU.Init (n);
        rep (i, 1, m) {
            // printf ("step = %d\n", i);
            // rep (j, 1, n) {
            //     printf (" dist[%d] = %intd\n", j, DSU.Query_Dist (DSU.timestamp, j));
            // }
            DSU.UnionSet (e[i].x, e[i].y);
            timestamp[i] = DSU.timestamp;
        }

        read (q, k, s);
        int lastans = 0;
        rep (i, 1, q) {
            int bgn, lne; read (bgn, lne);
            bgn = (bgn + k * lastans - 1) % n + 1;
            lne = (lne + k * lastans) % (s + 1);
            int l = 0, r = m + 1;
            while (l + 1 < r) {
                int mid = (l + r) >> 1;
                if (e[mid].h > lne) l = mid;
                else r = mid;
            }
            DSU.timestamp = timestamp[l];
            print ((lastans = DSU.Query_Dist (timestamp[l], DSU.FindSet (bgn))), '\n');
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值