Codeforces Round #225 (Div. 1) C. Propagating tree (dfs序 + 线段树)

原题链接:Problem - 383C - Codeforces

题目大意: 

        给你一颗根节点为 1 的树,每个点有一个点权,你要完成如下操作:

        - 操作1:向节点 x 加上一个值 val

        - 操作2:询问节点 x 的点权。

        这棵树还有一种神奇的性质:当你向节点 x 加上一个值 val 时, 节点 x 的子节点会加上 -val, 子节点的子节点会加上-(-val),如此反复直到树的底部。

解题思路:

        看到对树的子节点进行区间操作,首先想到线段树,对维护树上的信息,可以利用 树剖 或者 dfs序 来建树,然后再维护每一个节点的权值。但是观察到这一题并不是单纯的 区间加 或者 区间减 这么简单。

        题目中对于每一个节点加上一个值,他的子节点加上值的相反数,我们可以画出图来观察规律。

        比如对当前树的根加上一个值 val,那么对这棵树的影响就如下图:

      (红色为加,蓝色为减)

        

        我们首先考虑 深度 ,相对于根节点,深度每增大一次,那么 val 的值就会变成 -val。转化成容易处理的信息,就是:和根节点同为奇/偶数深度的点都加上 val ,为偶/奇数深度的点都加上-val。这里加的是根节点,如果是其他点同理。

        那么我们可不可以尝试利用这个性质维护懒标记呢?可以,但是要变换一下。

        尝试以根节点为标准( 深度为 1 ),和根节点同为奇数深度的点固定为加上值 val ,而为偶数深度的点固定为加上值 -val让每一次加减都是固定的,如下图:

        虽然这样,对于奇数点,可以维护好懒标记,我们可以获取正确的答案,但是对于偶数点呢?

         考虑固定的加减,那么当我们对图中偶数点(蓝色的点),要 加上值 val 时,我们按照刚才的固定加减,就会变成 加上值 -val

        那我们不是就可以把值 val 变成 wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==-val 吗?然后对于偶数点固定减,我们就会减去一个-val,也就是加上了 val 了啊!

        想到这一步,这一题也就出了,利用固定加减,如果当前是奇数,就向区间update(val)。偶数则就向区间 update(-val)。

        代码和具体解释如下:

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;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柠檬味的橙汁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值