[SDOI2017]树点涂色(LCT+线段树)
题目:洛谷P3703、LOJ#2001
题目描述:
给你一棵以\(1\)为根,有\(n\)个节点的树,初始时每个节点的颜色互不相同,记一条路径的权值为这条路径(包括起点和终点)上所有不同的颜色个数,一共\(m\)个操作,每次支持以下\(3\)种操作之一:
- 把点\(x\)到根节点的路径上所有的点染上一种没有用过的新颜色
- 求\(x\)到\(y\)的路径的权值
- 在以\(x\)为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值
\(n \leq 10^{5}\), \(m \leq 10^{5}\)
蒟蒻题解:
可以发现,这道题目根节点是固定的,同一种颜色一定是树上深度从小到大连续的一段
记\(v_{x}\)表示\(x\)到根节点的路径权值
则第\(2\)个操作中求的权值为\(v_{x}+v_{y}-v_{lca(x,y)}+1\)
方法一:重链剖分+线段树维护\(v_{x}\)
但是由于我最近在做\(lct\)专题,所以这边的做法是和\(lct\)有关
方法二:\(lct\)维护树上染色连通块+线段树维护\(v_{x}\)
通过这道题,我才知道\(lct\)维护树上染色连通块是什么意思其实就是直接维护,这道题加深了我对\(access\)的理解
我们可以另\(lct\)上每个\(splay\)表示一种染色连通块,这个\(splay\)上的点颜色都相同,且与这个颜色相同的点都在这个\(splay\)上
\(access(x)\)操作就是把根节点到\(x\)这条链染为同一种颜色,放在一个\(splay\)中,又由于\(lct\)操作是不会影响到这条链以外其他点之间的连通性的,把这条链提出来后,其他点该在哪个\(splay\)还是在哪个\(splay\)
线段树用来维护每个\(v_{x}\),修改全都是在线段树上修改,所以\(lct\)上不需要懒标记,最近都在打\(lct\),所以把线段树上的懒标记的\(int\)型定义成\(lct\)上区间翻转的\(bool\)型了,导致调了一个多小时
然后如下,\(access\)里面把\(x\)与\(son[x][1]\)之间的实边断开,再连虚边,它会对在原树上\(x\)在\(son[x][1]\)方向的子树产生影响,要找的是与\(x\)相连的点,而不是\(son[x][1]\),所以还要一个\(find_root(son[x][1])\)找\(son[x][1]\)所在\(splay\)的根,而虚边转实边也差不多
inline void access(int x)
{
for (Re i = 0; x; x = fa[i = x])
{
splay(x);
int u = son[x][1];
son[x][1] = 0;//先断开x与son[x][1]之间的实边,实边变虚边,后面才能找son[x][1]上与x相连的那个点
if (i)
{
int v = find_root(i);
modfy(1, 1, n, dfn[v], lst[v], -1);
}
if (u)
{
int v = find_root(u);//要找与x直接相连的那个点,即son[x][1]所在的splay的中序遍历的第一个点
modfy(1, 1, n, dfn[v], lst[v], 1);
}
son[x][1] = i;
}
}
由于\(lct\)的复杂度是\(\Theta(log\ n)\)的,线段树修改的时间复杂度也是\(\Theta(log\ n)\)的,所以总的期望复杂度是\(\Theta(nlog^{2} n)\)的
参考程序:
#include<bits/stdc++.h>
using namespace std;
#define Re register int
const int N = 100005;
int n, m, cnt, tim, dep[N], dfn[N], lst[N], g[N], hea[N], nxt[N << 1], to[N << 1], f[N][18], maxn[N << 2], lz[N << 2], fa[N], son[N][2];
inline int read()
{
char c = getchar();
int ans = 0;
while (c < 48 || c > 57) c = getchar();
while (c >= 48 && c <= 57) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar();
return ans;
}
inline void write(int x)
{
if (!x)
{
puts("0");
return;
}
int num = 0;
char sc[10];
while (x) sc[++num] = x % 10 + 48, x /= 10;
while (num) putchar(sc[num--]);
putchar('\n');
}
inline void add(int x, int y)
{
nxt[++cnt] = hea[x], to[cnt] = y, hea[x] = cnt;
}
inline void dfs(int x)
{
dep[x] = dep[f[x][0] = fa[x]] + 1, g[dfn[x] = ++tim] = x;
for (Re i = 1; f[x][i - 1]; ++i) f[x][i] = f[f[x][i - 1]][i - 1];
for (Re i = hea[x]; i; i = nxt[i])
{
int u = to[i];
if (u == fa[x]) continue;
fa[u] = x;
dfs(u);
}
lst[x] = tim;
}
inline int lca(int x, int y)
{
if (x == y) return x;
if (dep[x] < dep[y]) x ^= y ^= x ^= y;
for (Re i = 16; i >= 0; --i)
if (dep[f[x][i]] >= dep[y]) x = f[x][i];
if (x == y) return x;
for (Re i = 16; i >= 0; --i)
if (f[x][i] ^ f[y][i]) x = f[x][i], y = f[y][i];
return f[x][0];
}
inline void bui(int id, int l, int r)
{
if (l == r)
{
maxn[id] = dep[g[l]] - 1;
return;
}
int mid = l + r >> 1;
bui(id << 1, l, mid), bui(id << 1 | 1, mid + 1, r);
maxn[id] = max(maxn[id << 1], maxn[id << 1 | 1]);
}
inline void push_down(int id)
{
maxn[id << 1] += lz[id], maxn[id << 1 | 1] += lz[id], lz[id << 1] += lz[id], lz[id << 1 | 1] += lz[id], lz[id] = 0;
}
inline void modfy(int id, int l, int r, int x, int y, int z)
{
if (x <= l && r <= y)
{
lz[id] += z, maxn[id] += z;
return;
}
if (lz[id]) push_down(id);
int mid = l + r >> 1;
if (x <= mid) modfy(id << 1, l, mid, x, y, z);
if (y > mid) modfy(id << 1 | 1, mid + 1, r, x, y, z);
maxn[id] = max(maxn[id << 1], maxn[id << 1 | 1]);
}
inline int que(int id, int l, int r, int x, int y)
{
if (x <= l && r <= y) return maxn[id];
if (lz[id]) push_down(id);
int mid = l + r >> 1, ans = 0;
if (x <= mid) ans = que(id << 1, l, mid, x, y);
if (y > mid) ans = max(ans, que(id << 1 | 1, mid + 1, r, x, y));
return ans;
}
inline bool check(int x)
{
return son[fa[x]][0] == x || son[fa[x]][1] == x;
}
inline void rotate(int x)
{
int y = fa[x], z = fa[y];
bool t = son[y][1] ^ x;
if (check(y)) son[z][son[z][1] == y] = x;
fa[x] = z, fa[y] = x;
if (son[x][t]) fa[son[x][t]] = y;
son[y][t ^ 1] = son[x][t], son[x][t] = y;
}
inline void splay(int x)
{
while (check(x))
{
int y = fa[x];
if (check(y)) rotate(((son[fa[y]][0] == y) ^ (son[y][0] == x)) ? x : y);
rotate(x);
}
}
inline int find_root(int x)
{
splay(x);
while (son[x][0]) x = son[x][0];
return x;
}
inline void access(int x)
{
for (Re i = 0; x; x = fa[i = x])
{
splay(x);
int u = son[x][1];
son[x][1] = 0;//先断开x与son[x][1]之间的实边,实边变虚边,后面才能找son[x][1]上与x相连的那个点
if (i)
{
int v = find_root(i);
modfy(1, 1, n, dfn[v], lst[v], -1);
}
if (u)
{
int v = find_root(u);//要找与x直接相连的那个点,即son[x][1]所在的splay的中序遍历的第一个点
modfy(1, 1, n, dfn[v], lst[v], 1);
}
son[x][1] = i;
}
}
int main()
{
n = read(), m = read();
for (Re i = 1; i < n; ++i)
{
int u = read(), v = read();
add(u, v), add(v, u);
}
dfs(1);
bui(1, 1, n);
while (m--)
{
int opt = read(), u = read();
if (opt == 1) access(u);
else if (opt == 2)
{
int v = read(), w = lca(u, v);
int ans1 = que(1, 1, n, dfn[u], dfn[u]), ans2 = que(1, 1, n, dfn[v], dfn[v]), ans3 = que(1, 1, n, dfn[w], dfn[w]);
write(ans1 + ans2 - (ans3 << 1) + 1);
}
else write(que(1, 1, n, dfn[u], lst[u]) + 1);
}
return 0;
}

浙公网安备 33010602011771号