【bzoj4127】Abs 树链剖分+线段树
题目描述
给定一棵树,设计数据结构支持以下操作
1 u v d 表示将路径 (u,v) 加d
2 u v 表示询问路径 (u,v) 上点权绝对值的和
输入
第一行两个整数n和m,表示结点个数和操作数
接下来一行n个整数a_i,表示点i的权值
接下来n-1行,每行两个整数u,v表示存在一条(u,v)的边
接下来m行,每行一个操作,输入格式见题目描述
输出
对于每个询问输出答案
样例输入
4 4
-4 1 5 -2
1 2
2 3
3 4
2 1 3
1 1 4 3
2 1 3
2 3 4
样例输出
10
13
9
题解
树链剖分+线段树
考虑如果所有的数都是正数,或者是绝对值大于总增加量的负数(即无论怎么加都是负数),那么本体就是很水的树剖+线段树板子题。
然后有了能够从负数变为正数的数该怎么处理?
由于题目中只有加操作,没有减操作,所以一个数从负数变为正数最多只有一次。因此如果出现这种情况可以直接暴力修改,其余的按照上面的方法处理。
具体地,对于线段树的每一个节点,维护三个变量:$sum$、$si$和$val$,分别表示区间绝对值和、区间正数个数-负数个数的值、以及区间内的最大负数的绝对值(没有则为非正数)。
每次修改,如果区间内所有数不发生“由负变正”,则直接修改,并打上$lazy\ tag$;否则分裂成两个区间递归修改,直到某个区间长度为1且没有直接修改,说明其由负变正,直接修改它的值即可。
总的时间复杂度为$O(m\log^2 n+n\log n)$
#include <cstdio> #include <cstring> #include <algorithm> #define N 100010 #define lson l , mid , x << 1 #define rson mid + 1 , r , x << 1 | 1 using namespace std; typedef long long ll; const int inf = 1 << 30; int a[N] , head[N] , to[N << 1] , next[N << 1] , cnt , fa[N] , deep[N] , sv[N] , bl[N] , pos[N] , tot , v[N]; int n , val[N << 2] , si[N << 2] , add[N << 2]; ll sum[N << 2]; void addedge(int x , int y) { to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt; } void dfs1(int x) { int i; sv[x] = 1; for(i = head[x] ; i ; i = next[i]) if(to[i] != fa[x]) fa[to[i]] = x , deep[to[i]] = deep[x] + 1 , dfs1(to[i]) , sv[x] += sv[to[i]]; } void dfs2(int x , int c) { int i , k = 0; bl[x] = c , pos[x] = ++tot , v[tot] = a[x]; for(i = head[x] ; i ; i = next[i]) if(to[i] != fa[x] && sv[to[i]] > sv[k]) k = to[i]; if(k) { dfs2(k , c); for(i = head[x] ; i ; i = next[i]) if(to[i] != fa[x] && to[i] != k) dfs2(to[i] , to[i]); } } void pushup(int x) { int l = x << 1 , r = x << 1 | 1; sum[x] = sum[l] + sum[r]; if(val[l] > 0 && val[r] > 0) val[x] = min(val[l] , val[r]); else if(val[l] > 0) val[x] = val[l]; else if(val[r] > 0) val[x] = val[r]; else val[x] = 0; si[x] = si[l] + si[r]; } void pushdown(int x) { if(add[x]) { int l = x << 1 , r = x << 1 | 1; sum[l] += (ll)si[l] * add[x] , val[l] -= add[x] , add[l] += add[x]; sum[r] += (ll)si[r] * add[x] , val[r] -= add[x] , add[r] += add[x]; add[x] = 0; } } void build(int l , int r , int x) { if(l == r) { if(v[l] < 0) sum[x] = val[x] = -v[l] , si[x] = -1; else sum[x] = v[l] , val[x] = 0 , si[x] = 1; return; } int mid = (l + r) >> 1; build(lson) , build(rson); pushup(x); } void update(int b , int e , int a , int l , int r , int x) { if(b <= l && r <= e && (val[x] <= 0 || val[x] > a)) sum[x] += (ll)si[x] * a , val[x] -= a , add[x] += a; else if(l == r) sum[x] = a - sum[x] , val[x] = 0 , si[x] = 1; else { pushdown(x); int mid = (l + r) >> 1; if(b <= mid) update(b , e , a , lson); if(e > mid) update(b , e , a , rson); pushup(x); } } ll query(int b , int e , int l , int r , int x) { if(b <= l && r <= e) return sum[x]; pushdown(x); int mid = (l + r) >> 1; ll ans = 0; if(b <= mid) ans += query(b , e , lson); if(e > mid) ans += query(b , e , rson); return ans; } void modify(int x , int y , int z) { while(bl[x] != bl[y]) { if(deep[bl[x]] < deep[bl[y]]) swap(x , y); update(pos[bl[x]] , pos[x] , z , 1 , n , 1) , x = fa[bl[x]]; } if(deep[x] > deep[y]) swap(x , y); update(pos[x] , pos[y] , z , 1 , n , 1); } ll solve(int x , int y) { ll ans = 0; while(bl[x] != bl[y]) { if(deep[bl[x]] < deep[bl[y]]) swap(x , y); ans += query(pos[bl[x]] , pos[x] , 1 , n , 1) , x = fa[bl[x]]; } if(deep[x] > deep[y]) swap(x , y); ans += query(pos[x] , pos[y] , 1 , n , 1); return ans; } int main() { int m , i , opt , x , y , z; scanf("%d%d" , &n , &m); for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &a[i]); for(i = 1 ; i < n ; i ++ ) scanf("%d%d" , &x , &y) , addedge(x , y) , addedge(y , x); dfs1(1) , dfs2(1 , 1) , build(1 , n , 1); while(m -- ) { scanf("%d%d%d" , &opt , &x , &y); if(opt == 1) scanf("%d" , &z) , modify(x , y , z); else printf("%lld\n" , solve(x , y)); } return 0; }