bzoj4034树链剖分+线段树标记永久化

4034: [HAOI2015]树上操作

Time Limit: 10 Sec   Memory Limit: 256 MB
Submit: 5443   Solved: 1742
[ Submit][ Status][ Discuss]

Description

有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个
操作,分为三种:
操作 1 :把某个节点 x 的点权增加 a 。
操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
操作 3 :询问某个节点 x 到根的路径中所有点的点权和。

Input

第一行包含两个整数 N, M 。表示点数和操作数。接下来一行 N 个整数,表示树中节点的初始权值。接下来 N-1 
行每行三个正整数 fr, to , 表示该树中存在一条边 (fr, to) 。再接下来 M 行,每行分别表示一次操作。其中
第一个数表示该操作的种类( 1-3 ) ,之后接这个操作的参数( x 或者 x a ) 。

Output

对于每个询问操作,输出该询问的答案。答案之间用换行隔开。

Sample Input

5 5
1 2 3 4 5
1 2
1 4
2 3
2 5
3 3
1 2 1
3 5
2 1 2
3 3

Sample Output

6
9
13

HINT

 对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不会超过 10^6 。

Source

裸的树剖+线段树
用这题尝试了一下线段树标记永久化,蛮好玩的

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<cmath>
#define maxn 101001
#define ls p << 1
#define rs p << 1 | 1
using namespace std;
int read()
{
    char ch = getchar(); int x = 0, f = 1;
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}

int pre[maxn], top;
struct edge {
    int to, next;
    void add(int a, int b) {
        to = b;
        next = pre[a];
        pre[a] = top++;
    }
}e[maxn << 1];
int n, m, tot;
int f[maxn], d[maxn], in[maxn], out[maxn], deep[maxn], son[maxn], dson[maxn], w[maxn];
long long lazy[maxn * 8], sum[maxn * 8];
void adds(int u, int v)
{
    e[top].add(u, v);
    e[top].add(v, u);
}

void dfs1(int u, int fa) {
	f[u] = fa; deep[u] = deep[fa] + 1; son[u] = 1; dson[u] = 0;
	for(int i = pre[u]; ~i; i = e[i].next)
	{
		int v = e[i].to;
		if(v == fa) continue;
		dfs1(v, u);
		son[u] += son[v];
		if(son[v] > son[dson[u]]) dson[u] = v;
	}
}

void dfs2(int u, int chain) {
	out[u] = in[u] = ++tot; d[u] = chain;
	if(!dson[u]) return;
	dfs2(dson[u], chain);
	for(int i = pre[u]; ~i; i = e[i].next) 
		if(e[i].to != f[u] && e[i].to != dson[u]) 
			dfs2(e[i].to, e[i].to);
	out[u] = tot;
}

void Seg_ch(int p, int st, int ed, int L, int R, int val) {
	if(L == st && R == ed) {
		lazy[p] += val;
		return;
	}
	int mid = L + R >> 1; sum[p] += (long long)(ed - st + 1) * val;
	if(st <= mid) Seg_ch(ls, st, min(mid, ed), L, mid, val);
	if(ed > mid) Seg_ch(rs, max(mid + 1, st), ed, mid + 1, R, val);
}

long long Seg_sum(int p, int st, int ed, int L, int R) {
	if(L == st && R == ed) return sum[p] + lazy[p] * (ed - st + 1);
	int mid = L + R >> 1;
	long long ans = lazy[p] * (long long)(ed - st + 1);
	if(st <= mid) ans += Seg_sum(ls, st, min(mid, ed), L, mid);
	if(ed > mid) ans += Seg_sum(rs, max(mid + 1, st), ed, mid + 1, R);
	return ans;
}

void init() {
	memset(pre, -1, sizeof(pre)); top = 0;
	n = read(); m = read();
	for(int i = 1;i <= n; ++i) w[i] = read();
	for(int i = 1;i < n; ++i) adds(read(), read());
	dfs1(1, 0); dfs2(1, 1);
	for(int i = 1;i <= n; ++i) Seg_ch(1, in[i], in[i], 1 , n, w[i]);
}

long long query(int u) {
	long long ans = 0;
	while(u) {
		ans += Seg_sum(1, in[d[u]], in[u], 1, n);
		u = f[d[u]];
	}
	return ans;
}

void solve() {
	while(m--) {
		int opt = read(), x = read();
		if(opt == 1) Seg_ch(1, in[x], in[x], 1, n, read());
		if(opt == 2) Seg_ch(1, in[x], out[x], 1, n, read());
		if(opt == 3) printf("%lld\n", query(x));
	}
}

int main()
{
	init();
	solve();
	return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值