【学习笔记】可持久化并查集
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;
}