zoukankan      html  css  js  c++  java
  • BZOJ 3589 动态树 (树链剖分+线段树)

    前言

    众所周知,90%90\%的题目与解法毫无关系。


    题意

    有一棵有根树,两种操作。一种是子树内每一个点的权值加上一个同一个数,另一种是查询多条路径的并的点权之和。


    分析

    很容易看出是树链剖分+线段树的题目,唯一的问题就是多条路径可能有交集。那么我们只要把每条路径拆成多个部分,每一部分是某重链上连续的一段,就得到了很多区间。然后排序取并集就能在线段树上操作了。

    AC CODE

    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN = 200005;
    const int mod = 2147483647;
    int n, m;
    struct SegmentTree {
    	int v[MAXN<<2], lz[MAXN<<2], l[MAXN<<2], r[MAXN<<2], len[MAXN<<2];
    	inline void pushdown(int i) {
    		if(lz[i]) {
    			v[i<<1] += lz[i]*len[i<<1], lz[i<<1] += lz[i];
    			v[i<<1|1] += lz[i]*len[i<<1|1], lz[i<<1|1] += lz[i];
    			lz[i] = 0;
    		}
    	}
    	void build(int i, int L, int R) {
    		l[i] = L, r[i] = R, len[i] = R-L+1;
    		if(L == R) return;
    		build(i<<1, L, (L+R)>>1);
    		build(i<<1|1, (L+R)/2+1, R);
    	}
    	void Modify(int i, int L, int R, int k) {
    		if(L <= l[i] && r[i] <= R) { v[i] += k*len[i], lz[i] += k; return; }
    		pushdown(i);
    		int mid = (l[i] + r[i]) >> 1;
    		if(L <= mid) Modify(i<<1, L, R, k);
    		if(R > mid) Modify(i<<1|1, L, R, k);
    		v[i] = v[i<<1] + v[i<<1|1];
    	}
    	int Query(int i, int L, int R) {
    		if(L <= l[i] && r[i] <= R) return v[i];
    		pushdown(i);
    		int mid = (l[i] + r[i]) >> 1, res = 0;
    		if(L <= mid) res += Query(i<<1, L, R);
    		if(R > mid) res += Query(i<<1|1, L, R);
    		return res;
    	}
    }T;
    int fir[MAXN], to[MAXN<<1], nxt[MAXN<<1], cnt;
    inline void add(int x, int y) {
    	to[++cnt] = y; nxt[cnt] = fir[x]; fir[x] = cnt;
    }
    int dep[MAXN], top[MAXN], dfn[MAXN], hson[MAXN], sz[MAXN], fa[MAXN];
    inline void read(int &num) {
    	char ch; while((ch=getchar())<'0'||ch>'9');
    	for(num=0;ch>='0'&&ch<='9';num=num*10+ch-'0',ch=getchar());
    }
    struct node {
    	int l, r;
    	inline bool operator <(const node &t)const {
    		return l == t.l ? r > t.r : l < t.l;
    	}
    }a[MAXN], b[MAXN];
    int cur;
    inline void pre(int x, int y) {
    	while(top[x] != top[y]) {
    		if(dep[top[x]] < dep[top[y]]) swap(x, y);
    		a[++cur] = (node){ dfn[top[x]], dfn[x] };
    		x = fa[top[x]];
    	}
    	if(dep[x] < dep[y]) swap(x, y);
    	a[++cur] = (node){ dfn[y], dfn[x] };
    }
    inline int solve() {
    	sort(a + 1, a + cur + 1);
    	int tot = 0, res = 0, l = a[1].l, r = a[1].r;
    	for(int i = 1; i <= cur; ++i)
    		if(a[i].r > r) {
    			if(a[i].l > r+1) b[++tot] = (node){ l, r }, l = a[i].l, r = a[i].r;
    			else r = a[i].r;
    		}
    	b[++tot] = (node){ l, r };
    	for(int i = 1; i <= tot; ++i)
    		res = (res + T.Query(1, b[i].l, b[i].r)) & mod;
    	return res;
    }
    
    inline void dfs(int u, int ff) {
    	dep[u] = dep[fa[u]=ff] + (sz[u]=1);
    	for(int i = fir[u]; i; i = nxt[i])
    		if(to[i] != fa[u]) {
    			dfs(to[i], u), sz[u] += sz[to[i]];
    			if(sz[to[i]] > sz[hson[u]]) hson[u] = to[i];
    		}
    }
    
    inline void dfs2(int u, int tp) {
    	top[u] = tp; dfn[u] = ++cur;
    	if(hson[u]) dfs2(hson[u], tp);
    	for(int i = fir[u]; i; i = nxt[i])
    		if(to[i] != fa[u] && to[i] != hson[u])
    			dfs2(to[i], to[i]);
    }
    
    int main () {
    	read(n);
    	for(int i = 1, x, y; i < n; ++i)
    		read(x), read(y), add(x, y), add(y, x);
    	dfs(1, 0); dfs2(1, 1);
    	T.build(1, 1, n);
    	read(m);
    	int opt, x, y;
    	while(m--) {
    		read(opt);
    		if(!opt) read(x), read(y), T.Modify(1, dfn[x], dfn[x]+sz[x]-1, y);
    		else {
    			read(opt); cur = 0;
    			while(opt--) read(x), read(y), pre(x, y);
    			printf("%d
    ", solve());
    		}
    	}
    }
    

    UpdUpd

    不要问我中间为什么不取模。因为懒。能过就行


    EOFEOF

  • 相关阅读:
    Linux文件系统之INode
    手写Netty之多路复用Select小案例
    多路复用器Select、Poll、Epoll区别梳理
    NAT模式、路由模式、桥接模式的区别
    Netty编解码器(理论部分)
    Netty之Unpooled_Bytebuf
    为什么 TCP 协议有粘包问题
    IDEA_2019.1版本中Protobuf的使用
    Netty服务端Server代码说明
    Netty之ChannelHandler
  • 原文地址:https://www.cnblogs.com/Orz-IE/p/12039427.html
Copyright © 2011-2022 走看看