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

来源:互联网 发布:80端口被攻击 编辑:程序博客网 时间:2024/05/22 14:31

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

鸣谢bhiaibogf提供

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

#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 | 1using 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
原创粉丝点击