洛谷P4092 [HEOI2016/TJOI2016]树
标签
- 树链剖分
简明题意
- 给定一棵有根树,需要支持如下操作:
- 给某个节点打表记
- 查询一某个节点的离他最近的被标记的祖先(祖先包括自己)
思路
- 实际上我们可以去维护一条链中深度最深的且被标记的节点。当我们查询任意一个节点u时,我们只需要返回当前链中最深的节点对应的原始编号。然而线段树维护的是深度,我们只能查询到深度,那么如何通过深度得到编号呢?一种方法是得到链后,从链的末端向上跳 dep[末端]-查询到的深度 ,从而跳到应在的节点。这样会在最后一个测试点T掉。换一种方法,可以给线段树的每个节点一个max_id属性,表示当前区间最大深度对应的原始id。
注意事项
- 要注意的就是线段树要维护了一种信息,但你需要查询这种信息对应的另一种信息,然而这种信息自身对应着多种信息,我们想要查的是对应的且在同一条链上的信息。我们可以给线段树多开一个属性保存当前最大深度对应的原始编号即可
总结
- 无
AC代码
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 1e5 + 10;
int n, m, a[maxn];
vector<int> g[maxn];
int xxx;
int dep[maxn], fa[maxn], siz[maxn], son[maxn];
void dfs1(int u, int f, int deep)
{
dep[u] = deep;
fa[u] = f;
siz[u] = 1;
int maxson = -1;
for (auto& v : g[u])
if (v != f)
{
dfs1(v, u, deep + 1);
siz[u] += siz[v];
if (siz[v] > maxson)
maxson = v, son[u] = v;
}
}
int top[maxn], id[maxn], cnt;
void dfs2(int u, int topf)
{
id[u] = ++cnt;
top[u] = topf;
if (son[u])
{
dfs2(son[u], topf);
for (auto& v : g[u])
if (v != fa[u] && v != son[u])
dfs2(v, v);
}
}
struct Node
{
int l, r, max_dep;
int max_id;
bool operator < (const Node& a)const
{
return max_dep < a.max_dep;
}
};
Node tree[maxn * 4];
void build(int o, int l, int r)
{
tree[o].l = l, tree[o].r = r;
tree[o].max_dep = -1;
tree[o].max_id = r;
if (l == r)
return;
int mid = (tree[o].l + tree[o].r) / 2;
build(o * 2, l, mid);
build(o * 2 + 1, mid + 1, r);
}
void update(int o)
{
if (tree[o].l != tree[o].r)
{
tree[o].max_dep = max(tree[o * 2].max_dep, tree[o * 2 + 1].max_dep);
if (tree[o * 2].max_dep > tree[o * 2 + 1].max_dep)
tree[o].max_id = tree[o * 2].max_id;
else
tree[o].max_id = tree[o * 2 + 1].max_id;
}
}
void change(int o, int x)//节点x打标记
{
if (tree[o].l == tree[o].r)
{
tree[o].max_dep = dep[x];
tree[o].max_id = x;
return;
}
int mid = (tree[o].l + tree[o].r) / 2;
if (id[x] <= mid)
change(o * 2, x);
else
change(o * 2 + 1, x);
update(o);
}
Node ask(int o, int l, int r)
{
if (tree[o].l == l && tree[o].r == r)
return tree[o];
int mid = (tree[o].l + tree[o].r) / 2;
if (r <= mid)
return ask(o * 2, l, r);
else if (l > mid)
return ask(o * 2 + 1, l, r);
else
return max(ask(o * 2, l, mid), ask(o * 2 + 1, mid + 1, r));
}
int ask_fa(int u)
{
while (1)
{
Node k = ask(1, id[top[u]], id[u]);
if (k.max_dep != -1)
return k.max_id;
u = fa[top[u]];
if (u == 1)
return 1;
}
}
void solve()
{
scanf("%d%d", &n, &m);
for (int i = 1; i < n; i++)
{
int u, v;
scanf("%d%d", &u, &v);
g[u].push_back(v), g[v].push_back(u);
}
dfs1(1, 1, 1);
dfs2(1, 1);
build(1, 1, n);
change(1, 1);
while (m--)
{
char cmd[20];
int x;
scanf("%s%d", cmd, &x);
if (cmd[0] == 'Q')
printf("%d\n", ask_fa(x));
else if (cmd[0] == 'C')
change(1, x);
}
}
int main()
{
//freopen("Testin.txt", "r", stdin);
solve();
return 0;
}
双倍经验
- 无