原题链接:Problem - 383C - Codeforces
题目大意:
给你一颗根节点为 的树,每个点有一个点权,你要完成如下操作:
- 操作1:向节点 加上一个值
。
- 操作2:询问节点 的点权。
这棵树还有一种神奇的性质:当你向节点 加上一个值
时, 节点
的子节点会加上
, 子节点的子节点会加上
,如此反复直到树的底部。
解题思路:
看到对树的子节点进行区间操作,首先想到线段树,对维护树上的信息,可以利用 树剖 或者 dfs序 来建树,然后再维护每一个节点的权值。但是观察到这一题并不是单纯的 区间加 或者 区间减 这么简单。
题目中对于每一个节点加上一个值,他的子节点加上值的相反数,我们可以画出图来观察规律。
比如对当前树的根加上一个值 ,那么对这棵树的影响就如下图:
(红色为加,蓝色为减)
我们首先考虑 深度 ,相对于根节点,深度每增大一次,那么 的值就会变成
。转化成容易处理的信息,就是:和根节点同为奇/偶数深度的点都加上
,为偶/奇数深度的点都加上
。这里加的是根节点,如果是其他点同理。
那么我们可不可以尝试利用这个性质维护懒标记呢?可以,但是要变换一下。
尝试以根节点为标准( 深度为 ),和根节点同为奇数深度的点固定为加上值
,而为偶数深度的点固定为加上值
,让每一次加减都是固定的,如下图:
虽然这样,对于奇数点,可以维护好懒标记,我们可以获取正确的答案,但是对于偶数点呢?
考虑固定的加减,那么当我们对图中偶数点(蓝色的点),要 加上值 时,我们按照刚才的固定加减,就会变成 加上值
。
那我们不是就可以把值 变成
吗?然后对于偶数点固定减,我们就会减去一个
,也就是加上了
了啊!
想到这一步,这一题也就出了,利用固定加减,如果当前是奇数,就向区间update()。偶数则就向区间 update(
)。
代码和具体解释如下:
AC代码:
#include <bits/stdc++.h>
#define lson k << 1, l, mid
#define rson k << 1 | 1, mid + 1, r
using namespace std;
using i64 = long long;
const int N = 2e5 + 10;
vector<int> g[N];//vector存图
//in out为dfs序起点和终点 id为建树时回溯下标 idx维护dfs序
int in[N], out[N], d[N], id[N], idx;
i64 seg[N << 2], dep[N << 2], lazy[N << 2];
//seg为线段树 dep为树深度 lazy为懒标记
int arr[N];
inline void dfn(int u, int fa)//跑dfs序
{
d[u] = d[fa] + 1, id[++idx] = u, in[u] = idx;
for (auto& x : g[u])
if (x != fa) dfn(x, u);
out[u] = idx;
}
inline void pushdown(int k, int l, int r)
{
//lt为左儿子 rt为右儿子
int mid = l + r >> 1, lt = k << 1, rt = k << 1 | 1;
if (dep[lt] & 1) seg[lt] += lazy[k];//奇数固定加
else seg[lt] -= lazy[k];//偶数固定减
if (dep[rt] & 1) seg[rt] += lazy[k];//奇数固定加
else seg[rt] -= lazy[k];//偶数固定减
lazy[lt] += lazy[k], lazy[rt] += lazy[k], lazy[k] = 0;
}
inline void build(int k, int l, int r)//建树
{
if (l == r)
{
seg[k] = arr[id[l]];
dep[k] = d[id[l]];
return;
}
int mid = l + r >> 1;
build(lson), build(rson);
}
inline void upd(int k, int l, int r, int x, int y, int z)//更新
{
if (l >= x && r <= y) {
lazy[k] += z;
if (dep[k] & 1) seg[k] += z;//奇数固定加
else seg[k] -= z;//偶数固定减
return;
}
if (lazy[k]) pushdown(k, l, r);
int mid = l + r >> 1;
if (x <= mid) upd(lson, x, y, z);
if (y > mid) upd(rson, x, y, z);
}
inline i64 qry(int k, int l, int r, int x, int y)
{
if (l >= x && r <= y) return seg[k];
if (lazy[k]) pushdown(k, l, r);
int mid = l + r >> 1;
if (x <= mid) return qry(lson, x, y);
if (y > mid) return qry(rson, x, y);
}
void solve()
{
int n, q;
cin >> n >> q;
for (int i = 1; i <= n; ++i) cin >> arr[i];
for (int i = 1, x, y; i <= n - 1; ++i)
{
cin >> x >> y;
g[x].emplace_back(y);
g[y].emplace_back(x);
}
dfn(1, 0);
build(1, 1, n);
for (int i = 0, x; i < q; ++i)
{
i64 z;
cin >> x;
if (x == 1)
{
cin >> x >> z;
if (d[x] & 1) upd(1, 1, n, in[x], out[x], z);//奇数加上 z
else upd(1, 1, n, in[x], out[x], -z);//偶数加上 -z
}
else
{
cin >> x;
cout << qry(1, 1, n, in[x], in[x]) << '\n';
}
}
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int t = 1; //cin >> t;
while (t--) solve();
return 0;
}