zoukankan      html  css  js  c++  java
  • 【bzoj3307】雨天的尾巴 权值线段树合并

    题目描述

    N个点,形成一个树状结构。有M次发放,每次选择两个点x,y,对于x到y的路径上(含x,y)每个点发一袋Z类型的物品。完成所有发放后,每个点存放最多的是哪种物品。

    输入

    第一行数字N,M
    接下来N-1行,每行两个数字a,b,表示a与b间有一条边
    再接下来M行,每行三个数字x,y,z.如题

    输出

    输出有N行
    每i行的数字表示第i个点存放最多的物品是哪一种,如果有多种物品的数量一样,输出编号最小的。如果某个点没有物品则输出0

    样例输入

    3 2
    1 2
    1 3
    1 3 1
    2 3 2

    样例输出

    1
    2
    1


    题解

    权值线段树合并

    由于询问在修改之后,因此我们可以把修改差分,然后处理询问时合并标记即可。

    于是可以对于每个树上结点维护一棵线段树,维护区间最值和最值位置。

    先把操作的zi离散化,然后考虑差分:在xi和yi的线段树上将zi位置+1,在lca(xi,yi)和fa[lca(xi,yi)]的线段树上将zi位置-1.

    然后考虑标记的合并,可以使用线段树合并,复杂度为均摊$O(log n)$。

    最后从底向上合并标记并更新答案即可。

    时间复杂度为$O((n+m)log n)$。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 100010
    using namespace std;
    int head[N] , to[N << 1] , next[N << 1] , cnt , fa[N][18] , deep[N] , log[N] , x[N] , y[N] , z[N] , v[N];
    int m , ls[N * 60] , rs[N * 60] , mx[N * 60] , mp[N * 60] , root[N] , tot , ans[N];
    void add(int x , int y)
    {
    	to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
    }
    void dfs(int x)
    {
    	int i;
    	for(i = 1 ; (1 << i) <= deep[x] ; i ++ ) fa[x][i] = fa[fa[x][i - 1]][i - 1];
    	for(i = head[x] ; i ; i = next[i])
    		if(to[i] != fa[x][0])
    			fa[to[i]][0] = x , deep[to[i]] = deep[x] + 1 , dfs(to[i]);
    }
    int lca(int x , int y)
    {
    	int i;
    	if(deep[x] < deep[y]) swap(x , y);
    	for(i = log[deep[x] - deep[y]] ; ~i ; i -- )
    		if((1 << i) <= deep[x] - deep[y])
    			x = fa[x][i];
    	for(i = log[deep[x]] ; ~i ; i -- )
    		if((1 << i) <= deep[x] && fa[x][i] != fa[y][i])
    			x = fa[x][i] , y = fa[y][i];
    	return x == y ? x : fa[x][0];
    }
    void pushup(int x)
    {
    	if(mx[ls[x]] >= mx[rs[x]]) mx[x] = mx[ls[x]] , mp[x] = mp[ls[x]];
    	else mx[x] = mx[rs[x]] , mp[x] = mp[rs[x]];
    }
    void update(int p , int a , int l , int r , int &x)
    {
    	if(!x) x = ++tot;
    	if(l == r)
    	{
    		mx[x] += a , mp[x] = p;
    		return;
    	}
    	int mid = (l + r) >> 1;
    	if(p <= mid) update(p , a , l , mid , ls[x]);
    	else update(p , a , mid + 1 , r , rs[x]);
    	pushup(x);
    }
    int merge(int l , int r , int x , int y)
    {
    	if(!x) return y;
    	if(!y) return x;
    	if(l == r)
    	{
    		mx[x] += mx[y];
    		return x;
    	}
    	int mid = (l + r) >> 1;
    	ls[x] = merge(l , mid , ls[x] , ls[y]);
    	rs[x] = merge(mid + 1 , r , rs[x] , rs[y]);
    	pushup(x);
    	return x;
    }
    void solve(int x)
    {
    	int i;
    	for(i = head[x] ; i ; i = next[i])
    		if(to[i] != fa[x][0])
    			solve(to[i]) , root[x] = merge(1 , m , root[x] , root[to[i]]);
    	if(mx[root[x]]) ans[x] = v[mp[root[x]]];
    }
    int main()
    {
    	int n , i , a , b;
    	scanf("%d%d" , &n , &m);
    	for(i = 2 ; i <= n ; i ++ ) scanf("%d%d" , &a , &b) , add(a , b) , add(b , a) , log[i] = log[i >> 1] + 1;
    	dfs(1);
    	for(i = 1 ; i <= m ; i ++ ) scanf("%d%d%d" , &x[i] , &y[i] , &z[i]) , v[i] = z[i];
    	sort(v + 1 , v + m + 1);
    	for(i = 1 ; i <= m ; i ++ )
    	{
    		z[i] = lower_bound(v + 1 , v + m + 1 , z[i]) - v , a = lca(x[i] , y[i]);
    		update(z[i] , 1 , 1 , m , root[x[i]]) , update(z[i] , 1 , 1 , m , root[y[i]]);
    		update(z[i] , -1 , 1 , m , root[a]);
    		if(fa[a][0]) update(z[i] , -1 , 1 , m , root[fa[a][0]]);
    	}
    	solve(1);
    	for(i = 1 ; i <= n ; i ++ ) printf("%d
    " , ans[i]);
    	return 0;
    }
    

     

  • 相关阅读:
    (BFS 二叉树) leetcode 515. Find Largest Value in Each Tree Row
    (二叉树 BFS) leetcode513. Find Bottom Left Tree Value
    (二叉树 BFS DFS) leetcode 104. Maximum Depth of Binary Tree
    (二叉树 BFS DFS) leetcode 111. Minimum Depth of Binary Tree
    (BFS) leetcode 690. Employee Importance
    (BFS/DFS) leetcode 200. Number of Islands
    (最长回文子串 线性DP) 51nod 1088 最长回文子串
    (链表 importance) leetcode 2. Add Two Numbers
    (链表 set) leetcode 817. Linked List Components
    (链表 双指针) leetcode 142. Linked List Cycle II
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7289533.html
Copyright © 2011-2022 走看看