• P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并 题解


    solution

    对于树上的区间加操作,考虑转化为树上差分:对于两个节点 \(x,y\),只需在 \(sum_x+1,sum_y+1,sum_{LCA_{x,y}}-1,sum_{fa(LCA_{x,y})}-1\) 即可,只需一个倍增 LCA(好像不倍增也能过)。对于统计答案,由于线段树合并统计的是一个子树的信息,所以差分后一个子树的信息就是这单个根节点的信息。

    code

    #include <bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define I inline
    #define ll long long
    #define CI const int
    #define RI register int
    #define W while
    #define gc getchar
    #define D isdigit(c=gc())
    #define max(x,y) ((x)>(y)?(x):(y))
    #define min(x,y) ((x)<(y)?(x):(y))
    #define ms(a,x) memset((a),(x),sizeof(a))
    using namespace std;
    namespace SlowIO {
    	I void readc (char& c) {W (isspace (c = gc ()));}
    	Tp I void read (Ty& x) {char c; int f = 1; x = 0; W (! D) f = c ^ '-' ? 1 : -1; W (x = (x * 10) + (c ^ 48), D); x *= f;}
    	Ts I void read (Ty& x, Ar&... y) {read (x); read (y...);}
    } using namespace SlowIO;
    CI LGN = 50, N = 1e5, N2 = 2e5, N4 = 5e6; int n, m, anss[N + 5], MAX = N;
    namespace graph {
    	int nxt[N2 + 5], to[N2 + 5], hd[N + 5], cnt = 0;
    	void add (int u, int v) {++ cnt; to[cnt] = v; nxt[cnt] = hd[u]; hd[u] = cnt;}
    } using namespace graph;
    namespace FindLCA {
    	int lg[N + 5], dep[N + 5], fa[N + 5][LGN + 5];
    	void initLCA () {RI i, j; for (i = 1; i <= n; ++ i) lg[i] = lg[i - 1] + (1 << lg[i - 1] == i);}
    	void dfsfa (int now, int Fa) {
    		RI i, j; fa[now][0] = Fa; dep[now] = dep[Fa] + 1; for (i = 1; i <= lg[dep[now]]; ++ i) fa[now][i] = fa[fa[now][i - 1]][i - 1];
    		for (i = hd[now]; i; i = nxt[i]) if (to[i] != Fa) dfsfa (to[i], now);
    	}
    	int LCA (int x, int y) {
    		RI i, j; if (dep[x] < dep[y]) swap (x, y); W (dep[x] > dep[y]) x = fa[x][lg[dep[x] - dep[y]] - 1]; if (x == y) return x;
    		for (i = lg[dep[x]] - 1; i >= 0; -- i) if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i]; return fa[x][0];
    	}
    } using namespace FindLCA;
    namespace LineTree {
    	int ans[N4 + 5], sum[N4 + 5], sl[N4 + 5], sr[N4 + 5], rt[N + 5], ntot = 0;
    	void pushup (int root) {
    		if (sum[sl[root]] > sum[sr[root]]) sum[root] = sum[sl[root]], ans[root] = ans[sl[root]];
    		else if (sum[sr[root]] > sum[sl[root]]) sum[root] = sum[sr[root]], ans[root] = ans[sr[root]];
    		else sum[root] = sum[sl[root]], ans[root] = min (ans[sl[root]], ans[sr[root]]);
    	}
    	void update (int &root, int L, int R, int pos, int k) {
    		if (! root) root = ++ ntot; if (L == R) {ans[root] = L; sum[root] += k; return ;} int mid = (L + R) >> 1;
    		if (pos <= mid) update (sl[root], L, mid, pos, k); else update (sr[root], mid + 1, R, pos, k); pushup (root);
    	}
    	int merge (int a, int b, int L, int R) {
    		if (! a || ! b) return a + b; if (L == R) {ans[a] = ans[b]; sum[a] += sum[b]; return a;} int mid = (L + R) >> 1;
    		sl[a] = merge (sl[a], sl[b], L, mid); sr[a] = merge (sr[a], sr[b], mid + 1, R); pushup (a); return a;
    	}
    } using namespace LineTree;
    void dfs (int now, int Fa) {
    	RI i, j; for (i = hd[now]; i; i = nxt[i]) {if (to[i] != Fa) dfs (to[i], now), rt[now] = merge (rt[now], rt[to[i]], 1, MAX);} anss[now] = ans[rt[now]];
    }
    int main () {
    	RI i, j; for (read (n, m), initLCA (), i = 2; i <= n; ++ i) {int x, y; read (x, y); add (x, y); add (y, x);} dfsfa (1, 0); for (i = 1; i <= n; ++ i) rt[i] = i, ++ ntot;
    	for (i = 1; i <= m; ++ i) {
    		int x, y, z; read (x, y, z); update (rt[x], 1, MAX, z, 1); update (rt[y], 1, MAX, z, 1); int Lca = LCA (x, y); update (rt[Lca], 1, MAX, z, -1);
    		if (fa[Lca][0]) update (rt[fa[Lca][0]], 1, MAX, z, -1);
    	} dfs (1, 0); for (i = 1; i <= n; ++ i) printf ("%d \n", anss[i]);
    	return 0;
    }
    
  • 相关阅读:
    Redis学习笔记-安装篇(Centos7)
    图片上传预览方式,了解下?
    Angular中依赖注入方式的几种写法
    Javascript实现打开或退出浏览器全屏
    从头开始学Web开发—CSS_01
    JavaScript DOM知识 (一)
    javascript中的scroll事件
    javascript中继承的实现
    认识Javascript中的作用域和作用域链
    javascript中的闭包
  • 原文地址:https://www.cnblogs.com/ClapEcho233/p/16611323.html
Copyright © 2020-2023  润新知