zoukankan      html  css  js  c++  java
  • BZOJ 4127: Abs (树链剖分 线段树求区间绝对值之和 带区间加法)

    题意

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

    1 u v d  表示将路径 (u,v) 加d(d>=0)
    
    2 u v	表示询问路径 (u,v) 上点权绝对值的和
    

    分析

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

    当然考虑到一个位置要么在正树要么在负树,那么其实可以只写一棵线段树,如果当前区间最大负数+d仍然是负数就打上懒标记返回,否则暴力下传.线段树时间复杂度仍是O(nlogn)O(nlogn).加上树链剖分就是O(nlog2n)O(nlog^2n)啦.

    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
    ", Query(u, v));
    	}
    }
    
  • 相关阅读:
    POJ 1094 (传递闭包 + 拓扑排序)
    POJ 2594 (传递闭包 + 最小路径覆盖)
    POJ 3041 Asteroids(二分图最大匹配)
    差分数组原理与其前缀和的应用
    树状数组的理解以及简单应用
    HDU 4635 (完全图 和 有向图缩点)
    2.基本数据类型
    Python基础
    数据库类型
    Vue-2
  • 原文地址:https://www.cnblogs.com/Orz-IE/p/12039354.html
Copyright © 2011-2022 走看看