zoukankan      html  css  js  c++  java
  • BZOJ3307 雨天的尾巴

    题目蓝链

    Solution

    这道题我们先把(z)离散化一下。然后我们对于每一个操作,直接在树上差分一下,在路径的两端打上对(z)(1)的标记,在(lca)及其父亲处各打一个(-1)标记,然后直接线段树合并就可以了

    Code

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define squ(x) ((LL)(x) * (x))
    #define debug(...) fprintf(stderr, __VA_ARGS__)
    
    typedef long long LL;
    typedef pair<int, int> pii;
    
    inline int read() {
    	int sum = 0, fg = 1; char c = getchar();
    	for (; !isdigit(c); c = getchar()) if (c == '-') fg = -1;
    	for (; isdigit(c); c = getchar()) sum = (sum << 3) + (sum << 1) + (c ^ 0x30);
    	return fg * sum;
    }
    
    const int maxn = 1e5 + 10;
    
    struct node {
    	int x, y, z;
    }op[maxn];
    
    int n, m, len, t[maxn], rt[maxn], ans[maxn];
    
    namespace ST {
    
    	int cnt;
    	struct node {
    		int ls, rs;
    	}A[maxn << 6];
    	pii v[maxn << 6];
    
    	void push_up(int rt) { v[rt] = max(v[A[rt].ls], v[A[rt].rs]); }
    
    	int merge(int x, int y, int l, int r) {
    		if (!x || !y) return x | y;
    		if (l == r) { v[x].first += v[y].first; return x; }
    		int mid = (l + r) >> 1;
    		A[x].ls = merge(A[x].ls, A[y].ls, l, mid);
    		A[x].rs = merge(A[x].rs, A[y].rs, mid + 1, r);
    		push_up(x);
    		return x;
    	}
    
    	void change(int &rt, int l, int r, int x, int vv) {
    		if (!rt) rt = ++cnt;
    		if (l == r) {
    			v[rt].first += vv, v[rt].second = -t[l];
    			return;
    		}
    		int mid = (l + r) >> 1;
    		if (x <= mid) change(A[rt].ls, l, mid, x, vv);
    		else change(A[rt].rs, mid + 1, r, x, vv);
    		push_up(rt);
    	}
    
    	int query(int rt) {
    		if (!rt || !v[rt].first) return 0;
    		return -v[rt].second;
    	}
    
    }
    
    namespace Tree {
    
    	int Begin[maxn], Next[maxn << 1], To[maxn << 1], e;
    	void add_edge(int x, int y) {
    		To[++e] = y;
    		Next[e] = Begin[x];
    		Begin[x] = e;
    	}
    
    	int fa[maxn][17], d[maxn];
    
    	void input() {
    		e = -1;
    		memset(Begin, -1, sizeof Begin);
    		for (int i = 1; i < n; i++) {
    			int x = read(), y = read();
    			add_edge(x, y), add_edge(y, x);
    		}
    	}
    
    	void dfs(int now, int f) {
    		d[now] = d[f] + 1, fa[now][0] = f;
    		for (int i = 1; i <= 16; i++) fa[now][i] = fa[fa[now][i - 1]][i - 1];
    		for (int i = Begin[now]; i + 1; i = Next[i]) {
    			int son = To[i];
    			if (son == f) continue;
    			dfs(son, now);
    		}
    	}
    
    	int lca(int x, int y) {
    		if (d[x] < d[y]) swap(x, y);
    		for (int i = 16; ~i; i--)
    			if (d[fa[x][i]] >= d[y]) x = fa[x][i];
    		if (x == y) return x;
    		for (int i = 16; ~i; i--)
    			if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
    		return fa[x][0];
    	}
    
    	void add(int x, int y, int z) {
    		int LCA = lca(x, y), f = fa[LCA][0];
    		ST::change(rt[x], 1, len, z, 1);
    		ST::change(rt[y], 1, len, z, 1);
    		ST::change(rt[LCA], 1, len, z, -1);
    		ST::change(rt[f], 1, len, z, -1);
    	}
    
    	void merge(int now, int f) {
    		for (int i = Begin[now]; i + 1; i = Next[i]) {
    			int son = To[i];
    			if (son == f) continue;
    			merge(son, now);
    			rt[now] = ST::merge(rt[now], rt[son], 1, len);
    		}
    		ans[now] = ST::query(rt[now]);
    	}
    
    }
    
    int main() {
    #ifdef xunzhen
    	freopen("rain.in", "r", stdin);
    	freopen("rain.out", "w", stdout);
    #endif
    
    	n = read(), m = read();
    	Tree::input(), Tree::dfs(1, 0);
    	for (int i = 1; i <= m; i++) {
    		op[i].x = read(), op[i].y = read(), t[i] = op[i].z = read();
    	}
    	sort(t + 1, t + m + 1);
    	len = unique(t + 1, t + m + 1) - t - 1;
    	for (int i = 1; i <= m; i++) {
    		op[i].z = lower_bound(t + 1, t + len + 1, op[i].z) - t;
    		Tree::add(op[i].x, op[i].y, op[i].z);
    	}
    
    	Tree::merge(1, 0);
    
    	for (int i = 1; i <= n; i++) printf("%d
    ", ans[i]);
    
    	return 0;
    }
    

    Summary

    这是一道把树上差分和线段树合并结合起来的好题,像这种只有一次在最后询问的题一般都可以往线段树合并的方面去想

    (debug): 在离散化的时候,不要把原序列长度和离散化之后的长度搞混

  • 相关阅读:
    泛型总结
    Java多线程(学习篇)
    Java线程:总结
    Java线程:线程交互
    Java线程:线程安全类和Callable与Future(有返回值的线程)
    Java线程:条件变量、原子量、线程池等
    Java线程:堵塞队列与堵塞栈
    Java线程:锁
    poj 1679 The Unique MST(唯一的最小生成树)
    poj 1659 Frogs' Neighborhood (DFS)
  • 原文地址:https://www.cnblogs.com/xunzhen/p/9710203.html
Copyright © 2011-2022 走看看