NC23051 华华和月月种树
题目链接
关键点:
1、转换思想:即根据所有操作来建树,利用线段树。有关于将一点长出一个为权值为0的节点,可以当作将该权值清为0。
2、对于操作2,利用dfs序,维护出两个数组,一个l[N]表示该点的dfs序中的编号,r[N]表示该点dfs序的孩子节点的最后一个下标。那么对于区间加操作,可以利用dfs序维护出范围,然后求和。
3、对于操作3,可以利用树状数组来求该点的权值
4、线段树的tree值,也为lazy值。
完整代码:
# include <bits/stdc++.h>
using namespace std;
const int N = 400000+10;
int m;
int tree[N];
struct ty{
int type, x, num;
}op[N];
int l[N], r[N], n, cnt;
vector<int>edge[N];
void dfs(int x)
{
l[x] = ++cnt;
for (int i=0; i<edge[x].size(); i++)
{
int y = edge[x][i];
dfs(y);
}
r[x] = cnt;
}
void change(int p, int l, int r, int x)
{
if (l == r)
{
tree[p] = 0;
return ;
}
int mid = (l+r)/2;
if (tree[p] != 0)
tree[p*2] += tree[p], tree[p*2+1] += tree[p], tree[p] = 0;
if (x<=mid) change(p*2, l, mid, x);
else change(p*2+1, mid+1, r, x);
}
void add(int p, int l, int r, int x, int y, int num)
{
if (l>=x && r<=y)
{
tree[p] += num;
return ;
}
int mid = (l+r)/2;
if (tree[p] != 0)
tree[p*2] += tree[p], tree[p*2+1] += tree[p], tree[p] = 0;
if (x<=mid) add(p*2, l, mid, x, y, num);
if (y>mid) add(p*2+1, mid+1, r, x, y, num);
}
int find(int p, int l, int r, int x)
{
if (l == r)
{
return tree[p];
}
int mid = (l+r)/2;
if (tree[p] != 0)
tree[p*2] += tree[p], tree[p*2+1] += tree[p], tree[p] = 0;
if (x<=mid) return find(p*2, l, mid, x);
else return find(p*2+1, mid+1, r, x);
}
int main()
{
scanf("%d", &m);
n = 1;
for (int i=1; i<=m; i++)
{
int o;
scanf("%d", &o);
if (o == 1)
{
int x;
scanf("%d", &x);
edge[x].push_back(n);
op[i].type = 1;
op[i].x = n;
n++;
}
if (o == 2)
{
op[i].type = 2;
scanf("%d%d", &op[i].x, &op[i].num);
}
if (o == 3)
{
op[i].type = 3;
scanf("%d", &op[i].x);
}
}
dfs(0);
for (int i=1; i<=m; i++)
{
if (op[i].type == 1)
change(1, 1, n, l[op[i].x]);
if (op[i].type == 2)
add(1, 1, n, l[op[i].x], r[op[i].x], op[i].num);
if (op[i].type == 3)
printf("%d\n", find(1, 1, n, l[op[i].x]));
}
return 0;
}