BZOJ 4127: Abs (树链剖分 线段树求区间绝对值之和 带区间加法)

版权声明:本人版权意识薄弱,请随意转载 https://blog.csdn.net/Ike940067893/article/details/88785599

题意

给定一棵树,设计数据结构支持以下操作

1 u v d  表示将路径 (u,v) 加d(d>=0)

2 u v	表示询问路径 (u,v) 上点权绝对值的和

分析

绝对值之和不好处理,那么我们开两棵线段树,一个存正数,一个存负数.然后对于两棵线段树,都要维护子树sz(有效节点数),sum(有效节点权值之和),lz(加法懒标记).特别的,因为负数可能会加到正数,那么修改一个区间的时候,询问一下这个区间最大的负数加上d有没有变成正数,如果有就暴力从负数线段树中删去这个节点,加入正数线段树中.又题目中满足每一次加的数都是非负的,也就是说一个数最多从负变成正一次,那么时间复杂度均摊是 O ( n l o g n ) O(nlogn) 的(每次删除/插入是 l o g n logn 的).注意细节就行了…

CODE

看着我7000B的代码陷入沉思…为毛打个线段树板题都要220行+
(其实两棵树大多数函数可以直接复制)

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
char cb[1<<15],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<15,stdin),cs==ct)?0:*cs++)
template<class T>inline void read(T &res) {
    char ch; int flg = 1; for(;!isdigit(ch=getc());)if(ch=='-')flg=-flg;
    for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0'); res*=flg;
}
const int MAXN = 100005;
const LL INF = 1e16;
int n, m, cnt, tmr, w[MAXN], a[MAXN], fir[MAXN], fa[MAXN], dfn[MAXN], top[MAXN], sz[MAXN], son[MAXN], dep[MAXN];
struct edge { int to, nxt; }e[MAXN<<1];
inline void Add(int u, int v) { e[cnt] = (edge){ v, fir[u] }, fir[u] = cnt++; }
void dfs(int x, int ff) { //树剖
	dep[x] = dep[fa[x]=ff] + (sz[x]=1);
	for(int v, i = fir[x]; ~i; i = e[i].nxt)
		if((v=e[i].to) != ff) {
			dfs(v, x), sz[x] += sz[v];
			if(sz[v] > sz[son[x]]) son[x] = v;
		}
}
void dfs2(int x, int tp) {
	top[x] = tp; w[dfn[x] = ++tmr] = a[x];
	if(son[x]) dfs2(son[x], tp);
	for(int v, i = fir[x]; ~i; i = e[i].nxt)
		if((v=e[i].to) != fa[x] && v != son[x])
			dfs2(v, v);
}

struct node { //写个结构体存最大值以及坐标
	LL val; int id;
	node(){}
	node(LL v, int i):val(v), id(i){}
	inline node operator +(const node &o)const {
		if(val < o.val) return o;
		else return *this;
	}
};


namespace PositiveTree { //正线段树
	LL sum[MAXN<<2], lz[MAXN<<2]; int sz[MAXN<<2];
	inline void update(int i) {
		sum[i] = sum[i<<1] + sum[i<<1|1];
		sz[i] = sz[i<<1] + sz[i<<1|1];
	}
	inline void pushdown(int i) {
		if(lz[i]) {
			lz[i<<1] += lz[i]; sum[i<<1] += 1ll * sz[i<<1] * lz[i];
			lz[i<<1|1] += lz[i]; sum[i<<1|1] += 1ll * sz[i<<1|1] * lz[i];
			lz[i] = 0;
		}
	}
	void build(int i, int l, int r) {
		if(l == r) {
			sum[i] = w[l] >= 0 ? w[l] : 0;
			sz[i] = w[l] >= 0 ? 1 : 0;
			return;
		}
		int mid = (l + r) >> 1;
		build(i<<1, l, mid);
		build(i<<1|1, mid+1, r);
		update(i);
	}
	void insert(int i, int l, int r, int x, int y, int val) {
		if(x == l && y == r) {
			sum[i] += 1ll * sz[i] * val;
			lz[i] += val;
			return;
		}
		int mid = (l + r) >> 1;
		pushdown(i);
		if(y <= mid) insert(i<<1, l, mid, x, y, val);
		else if(x > mid) insert(i<<1|1, mid+1, r, x, y, val);
		else insert(i<<1, l, mid, x, mid, val), insert(i<<1|1, mid+1, r, mid+1, y, val);
		update(i);
	}
	void modify(int i, int l, int r, int x, int val) {
		if(l == r) {
			sum[i] = val, sz[i] = 1; return;
		}
		int mid = (l + r) >> 1;
		pushdown(i);
		if(x <= mid) modify(i<<1, l, mid, x, val);
		else modify(i<<1|1, mid+1, r, x, val);
		update(i);
	}
	LL querysum(int i, int l, int r, int x, int y) {
		if(x == l && y == r) return sum[i];
		int mid = (l + r) >> 1;
		pushdown(i);
		LL res;
		if(y <= mid) res = querysum(i<<1, l, mid, x, y);
		else if(x > mid) res = querysum(i<<1|1, mid+1, r, x, y);
		else res = querysum(i<<1, l, mid, x, mid) + querysum(i<<1|1, mid+1, r, mid+1, y);
		update(i);
		return res;
	}
}

namespace NegativeTree { //负线段树
	node mx[MAXN<<2];
	LL sum[MAXN<<2], lz[MAXN<<2]; int sz[MAXN<<2];
	inline void update(int i) {
		sum[i] = sum[i<<1] + sum[i<<1|1];
		mx[i] = mx[i<<1] + mx[i<<1|1];
		sz[i] = sz[i<<1] + sz[i<<1|1];
	}
	inline void pushdown(int i) {
		if(lz[i]) {
			lz[i<<1] += lz[i]; sum[i<<1] += 1ll * sz[i<<1] * lz[i]; mx[i<<1].val += lz[i];
			lz[i<<1|1] += lz[i]; sum[i<<1|1] += 1ll * sz[i<<1|1] * lz[i]; mx[i<<1|1].val += lz[i];
			lz[i] = 0;
		}
	}
	void build(int i, int l, int r) {
		if(l == r) {
			mx[i] = node(w[l] < 0 ? w[l] : -INF, l);
			sz[i] = w[l] < 0 ? 1 : 0;
			sum[i] = w[l] < 0 ? w[l] : 0; return;
		}
		int mid = (l + r) >> 1;
		build(i<<1, l, mid);
		build(i<<1|1, mid+1, r);
		update(i);
	}
	void insert(int i, int l, int r, int x, int y, int val) {
		if(x == l && y == r) {
			sum[i] += 1ll * sz[i] * val;
			lz[i] += val;
			mx[i].val += val;
			return;
		}
		int mid = (l + r) >> 1;
		pushdown(i);
		if(y <= mid) insert(i<<1, l, mid, x, y, val);
		else if(x > mid) insert(i<<1|1, mid+1, r, x, y, val);
		else insert(i<<1, l, mid, x, mid, val), insert(i<<1|1, mid+1, r, mid+1, y, val);
		update(i);
	}
	void modify(int i, int l, int r, int x) {
		if(l == r) {
			sum[i] = sz[i] = 0, mx[i].val = -INF; return;
		}
		int mid = (l + r) >> 1;
		pushdown(i);
		if(x <= mid) modify(i<<1, l, mid, x);
		else modify(i<<1|1, mid+1, r, x);
		update(i);
	}
	node querymx(int i, int l, int r, int x, int y) {
		if(x == l && y == r) return mx[i];
		int mid = (l + r) >> 1;
		pushdown(i);
		node res;
		if(y <= mid) res = querymx(i<<1, l, mid, x, y);
		else if(x > mid) res = querymx(i<<1|1, mid+1, r, x, y);
		else res = querymx(i<<1, l, mid, x, mid) + querymx(i<<1|1, mid+1, r, mid+1, y);
		update(i);
		return res;
	}
	LL querysum(int i, int l, int r, int x, int y) {
		if(x == l && y == r) return sum[i];
		int mid = (l + r) >> 1;
		pushdown(i);
		LL res;
		if(y <= mid) res = querysum(i<<1, l, mid, x, y);
		else if(x > mid) res = querysum(i<<1|1, mid+1, r, x, y);
		else res = querysum(i<<1, l, mid, x, mid) + querysum(i<<1|1, mid+1, r, mid+1, y);
		update(i);
		return res;
	}
	void ADD(int l, int r, int val) {
		if(r < l) return;
		node res = querymx(1, 1, n, l, r);
		if(res.val + val >= 0) {
			modify(1, 1, n, res.id); //暴力在负线段树中删
			PositiveTree::modify(1, 1, n, res.id, res.val + val); //暴力在正线段树中插入
			ADD(l, res.id-1, val);
			ADD(res.id+1, r, val);
		}
		else insert(1, 1, n, l, r, val);
	}
}

inline void Plus(int u, int v, int d) {
	while(top[u]!=top[v]) {
		if(dep[top[u]] < dep[top[v]]) swap(u, v);
		PositiveTree::insert(1, 1, n, dfn[top[u]], dfn[u], d);
		NegativeTree::ADD(dfn[top[u]], dfn[u], d);
		u = fa[top[u]];
	}
	if(dep[u] < dep[v]) swap(u, v);
	PositiveTree::insert(1, 1, n, dfn[v], dfn[u], d);
	NegativeTree::ADD(dfn[v], dfn[u], d);
}

inline LL Query(int u, int v) {
	LL res = 0;
	while(top[u]!=top[v]) {
		if(dep[top[u]] < dep[top[v]]) swap(u, v);
		res += - NegativeTree::querysum(1, 1, n, dfn[top[u]], dfn[u]) + PositiveTree::querysum(1, 1, n, dfn[top[u]], dfn[u]);
		u = fa[top[u]];
	}
	if(dep[u] < dep[v]) swap(u, v);
	return res - NegativeTree::querysum(1, 1, n, dfn[v], dfn[u]) + PositiveTree::querysum(1, 1, n, dfn[v], dfn[u]);
}

int main() {
	memset(fir, -1, sizeof fir);
	read(n), read(m);
	for(int i = 1; i <= n; ++i) read(a[i]);
	for(int x, y, i = 1; i < n; ++i)
		read(x), read(y), Add(x, y), Add(y, x);
	dfs(1, 0), dfs2(1, 1);
	NegativeTree::build(1, 1, n);
	PositiveTree::build(1, 1, n);
	int op, u, v, d;
	while(m--) {
		read(op), read(u), read(v);
		if(op == 1) read(d), Plus(u, v, d);
		else printf("%lld\n", Query(u, v));
	}
}

猜你喜欢

转载自blog.csdn.net/Ike940067893/article/details/88785599