最近写了几题LCT,算是复习和拓展吧,原来写的LCT都是静态的……下一步打算把QTREE系列都给写了吧……
LCT可以维护树上的路径,路径也即一个序列,于是就用Splay来维护这个路径序列。LCT支持查询树上两点之间的路径的各种信息(基本上Splay能存的LCT都行),并且可以改变树的形态,复杂度都是均摊O(logN)的。
关于LCT的介绍网上到处都是,论文都有好几篇……LCT的操作大概有下面几个:expose(也有叫access的,但感觉expose要霸气一些……)、query、modify、link、cut、makeRoot。
expose x的作用是提出从x到根的路径,后面的操作中基本上都会用到expose。
query和modify这是对路径上的权值进行修改,expose之后用Splay操作就行了。
makeRoot x即换根。换根影响的节点只有从x到当前根的路径上的节点,只需expose x再splay x,之后把Splay中的x节点打上翻转标记即可。
link x y即在两点之间连边。先makeRoot x,再把x的父节点指向y即可。
cut x y即删去x和y之间的边。要考虑x是y的父亲和y是x的父亲的两种情况,expose x再splay y,如果此时y的父节点为x则说明x是y的父亲,清除y的父节点指针。不然再反着来一遍。
cut操作之所以要这样是有理由的。如果x是y的父亲那么expose x之后提出的就是x到根的路径,上面一定不含y,而且此时x的左右儿子也都不是y。这时候再splay y就可以直接清除标记了。
另外注意,即使x和y没有直接相连,cut x y还是会使x和y不连通。所以假如题目没有保证给定的x和y一定直接相连,那么要先判断。可以用set维护与每个点相连的点,或者expose y再splay y,然后检查y的近根端的最远点(即y的前驱)是否为x;如果不是就反着来一遍。
题目:
BZOJ1036
给定一棵树,每个点有点权。操作有:1、询问两点之间路径上的最大权;2、询问两点之间路径上的权值和,3、修改点权。
裸的LCT。
BZOJ2002
给定一个序列S1~Sn。Si代表如果到了i号位置,那么下一次就到i+Si号位置,如果i+Si>n则跳出了序列。操作有:1、询问从i号位置出发,要跳几次才能跳出序列;2、修改Si。
对于每个Si如果i+Si<=n那么从i+Si点向i点连边,那么询问即求从某点到其根的距离,修改则是改变树的形态。求到根的距离实际上就是求从该点到根的路径上的点的个数(显然……),答案即expose之后左子树的大小+1。
BZOJ2049
给定N个点,一开始没有边。操作有:1、在x和y之间连边;2、摧毁x和y之间的边(保证之前有在x和y之间连边的操作);3、询问x和y是否在一个连通块内。保证任意时刻N个点构成森林。
连边和删边都是形态变换,询问是否连通则可以类似query做。令expose x返回最后一次虚边变为实边时父节点的编号,那么expose x再expose y返回的就是LCA(前提是x和y连通)。如果沿着这个“LCA”一路向右能走到x的话那么x和y连通,否则不连通。
SPOJ QTREE
给定一棵树,每条边有边权。操作有:1、询问两点之间路径上的最大权;2、修改边权。
随便选一个根,将边权赋给边的两个端点中离根较远的点上,然后就是裸LCT了。
SPOJ QTREE2
给定一棵树,每条边有边权。操作有:1、询问两点之间路径的长度;2、询问一点到另一点路径上的第k个点。
询问2即expose提出的链上的第k个点,用Splay的find操作即可。注意判断第k个点在是在x的支链上还是y的支链上。
BZOJ2594
给定一个N个点M条边的无向图,每条边有边权。操作有:1、询问两点之间所有路径中最大边权最小的路径上的最大边权;2、删去一条边。
这题是WC2006撸水管局长的数据加强版。首先明确,操作1询问的最大边权的边一定在整个图的最小生成树上(考虑之前的删边操作)。
接下来就是如何处理删边。如果直接处理,似乎好找要加入哪条边。那么我们按倒序离线处理询问,把删边转换为加边。假设加上边(x,y),权值为w,那么找到LCT上x到y的路径上权值最大的边,如果改边的权值>w,那么删去该边,然后link x y。
不过由于这里要改变树的形态,不太好把边权压到点上,所以可以将每条边视为一个点,那么边e(x,y)在LCT中即x->e->y。把点权设为0就不会影响查询操作。
代码:
只发BZOJ2594的,所有的操作这里面都有……感觉这个模板应该不算慢吧,加了FastIO在BZOJ上跑了11s……
//BZOJ2594; 水管局长STRONG_DATA (WC2006); Link-Cut Tree
#include
#include
#include
#define N 100000
#define M 1000000
#define Q 100000
#define SIZE 33000000
using namespace std;
class inedge
{
public:
int a, b, w, num;
bool v;
inedge() : v(false) {}
inedge(int _a, int _b, int _w) : a(_a), b(_b), w(_w), v(false) {}
inedge(int _a, int _b, int _w, int _num) : a(_a), b(_b), w(_w), num(_num), v(false) {}
}e[M + 1], qt[Q + 1];
class Node
{
public:
Node *l, *r, *f, *p;
int m, v, num;
bool rev;
Node() {}
Node(int _v, int _num) : l(NULL), r(NULL), f(NULL), rev(false), v(_v), m(_v), p(this), num(_num) {}
}*p[N << 1 | 1];
bool op[Q + 1];
int n, m, q, tot, cnt, x, y, w, ans[Q + 1], f[N + 1];
char inbuf[SIZE], *ip = inbuf;
inline int getint()
{
int r = 0;
while (*ip < '0' || *ip > '9') ++ip;
while (*ip >= '0' && *ip <= '9') r = r * 10 + *(ip++) - '0';
return r;
}
inline bool cmp(const inedge &x, const inedge &y)
{ return x.a == y.a ? x.b < y.b : x.a < y.a; }
inline bool cmp2(const inedge &x, const inedge &y)
{ return x.w < y.w; }
int find(int x)
{ return x == f[x] ? x : f[x] = find(f[x]); }
inline bool isRoot(Node *x)
{ return !(x->f) || (x->f->l != x && x->f->r != x); }
inline void push(Node *x)
{
if (!(x->rev)) return ;
x->rev = false;
if (x->l) x->l->rev ^= 1;
if (x->r) x->r->rev ^= 1;
swap(x->l, x->r);
}
inline void update(Node *x)
{
x->m = x->v, x->p = x;
if (x->l && x->l->m > x->m) x->m = x->l->m, x->p = x->l->p;
if (x->r && x->r->m > x->m) x->m = x->r->m, x->p = x->r->p;
}
inline void zig(Node *x)
{
Node *y = x->f;
Node *z = y->f;
push(x);
if (z && z->l == y) z->l = x;
else if (z && z->r == y) z->r = x;
if (x->r) x->r->f = y;
y->l = x->r;
x->r = y, y->f = x, x->f = z;
update(y);
}
inline void zag(Node *x)
{
Node *y = x->f;
Node *z = y->f;
push(x);
if (z && z->l == y) z->l = x;
else if (z && z->r == y) z->r = x;
if (x->l) x->l->f = y;
y->r = x->l;
x->l = y, y->f = x, x->f = z;
update(y);
}
inline void splay(Node *x)
{
Node *y, *z;
while (!isRoot(x))
{
y = x->f;
z = y->f;
if (z) push(z);
push(y);
if (isRoot(y))
if (y->l == x) zig(x);
else zag(x);
else
if (z->l == y)
if (y->l == x) zig(y), zig(x);
else zag(x), zig(x);
else
if (y->l == x) zig(x), zag(x);
else zag(y), zag(x);
}
push(x);
update(x);
}
inline void expose(Node *x)
{
for (Node *y = NULL; x; x = x->f)
{
splay(x);
x->r = y;
update(y = x);
}
}
inline void makeRoot(Node *x)
{
expose(x);
splay(x);
x->rev ^= 1;
}
inline void cut(Node *x, Node *y)
{
expose(y);
splay(x);
if (x->f == y)
{
x->f = NULL;
if (y->l == x) y->l = NULL;
else if (y->r == x) y->r = NULL;
}
else
{
expose(x);
splay(y);
y->f = NULL;
if (x->l == y) x->l = NULL;
else if (x->r == y) x->r = NULL;
}
}
inline void link(Node *x, Node *y)
{
makeRoot(x);
x->f = y;
}
inline int findedge(int x, int y)
{ return lower_bound(e + 1, e + m + 1, inedge(x, y, 0), cmp) - e; }
inline Node* query(Node *x, Node *y)
{
Node *r = NULL;
expose(y);
for (y = NULL; x; x = x->f)
{
splay(x);
if (!(x->f))
{
if (x->r && (!r || x->r->m > r->m)) r = x->r->p;
if (y && (!r || y->m > r->m)) r = y->p;
}
x->r = y;
update(y = x);
}
return r;
}
inline void modify(inedge &c)
{
Node *x = p[c.a], *y = p[c.b], *r;
r = query(x, y);
if (r->v <= c.w) return ;
cut(r, p[e[r->num].a]);
cut(r, p[e[r->num].b]);
delete r;
r = new Node(c.w, c.num);
link(r, x);
link(r, y);
}
int main()
{
// freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
fread(inbuf, sizeof(char), sizeof(char) * SIZE, stdin);
// scanf("%d%d%d", &n, &m, &q);
n = getint(), m = getint(), q = getint();
for (int i = 1; i <= n; ++i) p[i] = new Node(0, -i), f[i] = i;
for (int i = 1; i <= m; ++i)
{
// scanf("%d%d%d", &x, &y, &w);
x = getint(), y = getint(), w = getint();
if (x > y) swap(x, y);
e[i] = inedge(x, y, w);
}
sort(e + 1, e + m + 1, cmp);
for (int i = 1; i <= m; ++i) e[i].num = i;
for (int i = 1; i <= q; ++i)
{
// scanf("%d%d%d", &w, &x, &y);
w = getint(), x = getint(), y = getint();
if (x > y) swap(x, y);
qt[i] = inedge(x, y, 0);
op[i] = w == 1;
if (w == 1) continue;
int pos = findedge(x, y);
e[pos].v = true, qt[i].num = pos, qt[i].w = e[pos].w;
}
tot = m;
for (int i = 1; i <= tot; ++i)
if (e[i].v) swap(e[i--], e[tot--]);
sort(e + 1, e + tot + 1, cmp2);
for (int i = 1; i <= tot; ++i) e[i].v = false;
cnt = n;
for (int i = 1; i <= tot; ++i)
if (find(e[i].a) != find(e[i].b))
{
p[++cnt] = new Node(e[i].w, e[i].num);
link(p[e[i].a], p[cnt]);
link(p[cnt], p[e[i].b]);
f[find(e[i].a)] = find(e[i].b);
e[i].v = true;
}
sort(e + 1, e + m + 1, cmp);
for (int i = q; i > 0; --i)
if (op[i])
ans[i] = query(p[qt[i].a], p[qt[i].b])->v;
else modify(qt[i]);
for (int i = 1; i <= q; ++i)
if (op[i])
printf("%d\n", ans[i]);
return 0;
}