zoukankan      html  css  js  c++  java
  • 线段树合并(雨天的尾巴 题解)

    线段树合并

    前置知识:权值线段树,动态开点;
    其实动态开点就是当前节点的左右子区间的节点编号不是单纯的2倍和2倍加1的关系;
    我们需要存储一下左右子区间的节点编号,就可以了;
    线段树合并,顾名思义,就是,将两颗值域相同权值线段树合并为一棵线段树,并且对于相同节点
    相加的一些操作,具体在代码中讲解;
    模板题
    雨天的尾巴

    #include<iostream>
    #include<cstdio>
    #define l(o) (t[o].ls)
    #define r(o) (t[o].rs)
    #define mid ((l+r)>>1)
    using namespace std;
    const int N=1e5+7;
    const int M=1e5;
    struct edge{
    	int v,nxt;
    }e[N<<1];
    struct Setment{
    	int ls,rs,dat,id;
    }t[N*20*4];//注意线段树大小是不确定的,一般开这么多
    int n,m,cnt,tot;
    int head[N],dep[N],top[N],siz[N],hs[N],fa[N],ans[N],rt[N];
    void add_edge(int u,int v){
    	e[++cnt]=(edge){v,head[u]};
    	head[u]=cnt;
    }
    void get_tree(int u){
    	dep[u]=dep[fa[u]]+1;
    	siz[u]=1;
    	for(int i=head[u];i;i=e[i].nxt){
    		int to=e[i].v;
    		if(to!=fa[u]){
    			fa[to]=u;
    			get_tree(to);
    			siz[u]+=siz[to];
    			if(siz[hs[u]]<siz[to]) hs[u]=to;
    		}
    	}
    }
    void dfs(int u,int fat){
    	top[u]=fat;
    	if(hs[u]) dfs(hs[u],fat);
    	for(int i=head[u];i;i=e[i].nxt){
    		int to=e[i].v;
    		if(to!=fa[u]&&to!=hs[u]){
    			dfs(to,to);
    		}
    	}
    }
    
    int lca(int x,int y){
    	while(top[x]!=top[y]){
    		if(dep[top[x]]<dep[top[y]]) swap(x,y);
    		x=fa[top[x]];
    	}
    	return dep[x]<dep[y]?x:y;
    }
    
    //以上为求lca操作
    void up(int o){
    	t[o].dat=max(t[l(o)].dat,t[r(o)].dat);
    	t[o].id=t[o].dat==t[l(o)].dat?t[l(o)].id:t[r(o)].id;//尽量选小的
    }
    
    void insert(int &o,int l,int r,int k,int val){
    	if(!o) o=++tot;//动态开点,注意这里的取地址符号,是为了把编号存下来
    	if(l==r){
    		t[o].dat+=val;
    		t[o].id=k;
    		return;
    	}
    	if(k<=mid) insert(l(o),l,mid,k,val);
    	if(k>mid) insert(r(o),mid+1,r,k,val);
    	up(o);
    }
    
    
    void merge(int &x,int y,int l,int r){
    	if(!x) {
    		x=y;
    		return;
    	}
    	if(!y) return;
    	if(l==r){
    		t[x].dat+=t[y].dat;
    		return;
    	}
    	merge(t[x].ls,t[y].ls,l,mid);
    	merge(t[x].rs,t[y].rs,mid+1,r);
    	up(x);
    }
    
    void get_ans(int u){
    	for(int i=head[u];i;i=e[i].nxt){
    		int to=e[i].v;
    		if(to!=fa[u]){
    			get_ans(to);
    			merge(rt[u],rt[to],1,M);
    		}
    	}
    	ans[u]=t[rt[u]].id;
    }
    
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<n;i++){
    		int x,y;
    		scanf("%d%d",&x,&y);
    		add_edge(x,y);
    		add_edge(y,x);
    	}
    	get_tree(1);
    	dfs(1,1);
    	for(int i=1;i<=m;i++){
    		int x,y,z;
    		scanf("%d%d%d",&x,&y,&z);
    //		cout<<"ffff"<<"
    ";
    		int ff=lca(x,y);
    //		cout<<"kkkkk"<<"
    ";
    		insert(rt[x],0,M,z,1);//rt[]数组表示为以x为根节点的线段树的最大区间;
    		insert(rt[y],0,M,z,1);
    		insert(rt[ff],0,M,z,-1);
    		insert(rt[fa[ff]],0,M,z,-1);//点差分
    	}
    	get_ans(1);
    	for(int i=1;i<=n;i++) cout<<ans[i]<<"
    ";
    }
    /*
    10 10 
    2 1 
    3 2 
    4 3 
    5 3 
    6 3 
    7 4 
    8 5
    9 8 
    10 3 
    6 2 6 
    3 2 6 
    3 3 2 
    6 6 6 
    10 3 3 
    10 7 1 
    3 7 4 
    7 9 5 
    4 2 4 
    3 3 6
    
    0
    6
    6
    4
    5
    6
    1
    5
    5
    1
    */
    
  • 相关阅读:
    数组、List和ArrayList的区别
    Spring 学习笔记(一)——Spring 基本概念和体系概览
    解压大文件提示C盘空间不够的问题
    在Struts2 Action中快速简便的访问Request、Session等变量
    Java 以及JEE环境快速搭建
    JAVA对象布局之对象头(Object Header)
    Docker之两小时入门
    Java创建多线程的几种方式
    JAVA并发之加锁导致的活跃性问题
    JAVA并发之多线程引发的问题剖析以及如何保证线程安全
  • 原文地址:https://www.cnblogs.com/Aswert/p/13856810.html
Copyright © 2011-2022 走看看