树链剖分
安装操作:
将1到x的点权统统_覆盖_为1,操作前覆盖一次,操作后覆盖一次。然后分别记录这两次1到x的路径点权和,求他们的差即是答案。
卸载操作:
输出x的子树点权值和,然后把x的子树点权统统_覆盖_为0。
覆盖操作可以用ODT,线段树,线段树的lazy数组初始值要赋为-1,因为有区间赋值为0的操作。
#include <bits/stdc++.h>
#define N 1000115
#define ls l, mid, root << 1
#define rs mid + 1, r, root << 1 | 1
using namespace std;
int n, m, tot, cnt, lin[N], dep[N], fat[N], siz[N], son[N], top[N], dfn[N], tree[N], lazy[N];
struct edg {
int to, nex;
}e[N];
inline void add(int f, int t)
{
e[++cnt].nex = lin[f];
e[cnt].to = t;
lin[f] = cnt;
}
void dfs1(int now, int fa)
{
dep[now] = dep[fa] + 1;
fat[now] = fa;
siz[now] = 1;
for (int i = lin[now]; i; i = e[i].nex)
{
int to = e[i].to;
if (to == fa) continue;
dfs1(to, now);
siz[now] += siz[to];
if (siz[to] > siz[son[now]])
son[now] = to;
}
}
void dfs2(int now, int tes)
{
top[now] = tes;
dfn[now] = ++tot;//dfn是dfs序
if (!son[now]) return;
dfs2(son[now], tes);
for (int i = lin[now]; i; i = e[i].nex)
{
int to = e[i].to;
if (to == fat[now] || to == son[now]) continue;
dfs2(to, to);
}
}
inline void pushup(int root)
{
tree[root] = tree[root << 1] + tree[root << 1 | 1];
}
inline void pushdown(int l, int r, int root)
{
if (lazy[root] != -1)
{
int mid = (l + r) >> 1;
lazy[root << 1] = lazy[root]; lazy[root << 1 | 1] = lazy[root];
tree[root << 1] = (mid - l + 1) * lazy[root], tree[root << 1 | 1] = (r - mid) * lazy[root];
lazy[root] = -1;
}
}
void build(int l, int r, int root)
{
if (l == r)
{
tree[root] = 0;
lazy[root] = -1;
return;
}
int mid = (l + r) >> 1;
build(ls), build(rs);
pushup(root);
}
void update(int ql, int qr, int add, int l, int r, int root)
{
if (ql <= l && r <= qr)
{
tree[root] = (r - l + 1) * add;//此处为赋值操作
lazy[root] = add;
return;
}
int mid = (l + r) >> 1;
pushdown(l, r, root);
if (ql <= mid)
update(ql, qr, add, ls);
if (qr > mid)
update(ql, qr, add, rs);
pushup(root);
}
int query(int ql, int qr, int l, int r, int root)
{
int res = 0;
if (ql <= l && r <= qr)
return tree[root];
int mid = (l + r) >> 1;
pushdown(l, r, root);
if (ql <= mid)
res += query(ql, qr, ls);
if (qr > mid)
res += query(ql, qr, rs);
return res;
}
int Q(int a, int b)//a, b的路径
{
int res = 0;
while (top[a] != top[b])
{
if (dep[top[a]] < dep[top[b]])
swap(a, b);
res += query(dfn[top[a]], dfn[a], 1, n, 1);
a = fat[top[a]];//处理了一个链。
}
if (dep[a] > dep[b]) swap(a, b);
res += query(dfn[a], dfn[b], 1, n, 1);
return res;
}
void U(int a, int b)
{
while (top[a] != top[b])
{
if (dep[top[a]] < dep[top[b]])
swap(a, b);
update(dfn[top[a]], dfn[a], 1, 1, n, 1);
a = fat[top[a]];//处理了一个链。
}
if (dep[a] > dep[b]) swap(a, b);
update(dfn[a], dfn[b], 1, 1, n, 1);
}
int main()
{
//in:先输出0到他的路径边权和,安装是将其路径上的所有边权变为1,
//un:求出子树得值,将他的子树全都赋为一个数,求出子树的值,将他的子树全都赋为一个数
scanf("%d", &n);
for (int i = 2, a; i <= n; i++)
scanf("%d", &a), add(a + 1, i), add(i, a + 1);//先使所有点的编号加1
dfs1(1, 0);
dfs2(1, 1);
scanf("%d", &m);
while (m--)
{
string s; int a;
cin >> s; scanf("%d", &a);
a++;
if (s[0] == 'i')//安装
{
int a1 = Q(1, a);//查询1到他的路径和。
U(1, a);//更改1,a的路径和统统赋为1。
int a2 = Q(1, a);
printf("%d
", a2 - a1);
}
else
{
printf("%d
", query(dfn[a], dfn[a] + siz[a] - 1, 1, n, 1));//查询子树的和
update(dfn[a], dfn[a] + siz[a] - 1, 0, 1, n, 1);
}
}
return 0;
}