zoukankan      html  css  js  c++  java
  • 洛谷 P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并

    传送门
    线段树合并板子题,但是好像用树链剖分和树上启发式合并都能做,之后再想想吧。
    其实写过之后才知道,线段树合并这个操作和fhqtreap的合并很像,基本能在时间复杂度 (O(logn)) 里做到。
    要注意的是,线段树合并虽然时空复杂度理论都是 (nlogn),但是时空常数都是巨大,空间一般要开足 (3nlogn),时间可能比 (n(logn)^2) 的算法更慢,淦。
    其他就没什么要注意的了,如果写过主席树和fhqtreap,线段树合并的操作基本都能理解。
    这道题借助树上差分的原理,然后将每个节点的线段树和子节点合并。

    #include <bits/stdc++.h>
    using namespace std;
    const int N=1e5+10;
    int n,m,fa[N][22],dep[N],root[N],ans[N];
    int head[N],to[N*2],nxt[N*2],tot;
    void add(int u,int v){to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}
    struct SegTrees{
    	#define mid (l+r>>1)
    	int maxv[N*50],maxp[N*50],ls[N*50],rs[N*50],tot;
    	void pushup(int id){
    		if(maxv[ls[id]]>=maxv[rs[id]]) maxv[id]=maxv[ls[id]],maxp[id]=maxp[ls[id]];
    		else maxv[id]=maxv[rs[id]],maxp[id]=maxp[rs[id]];
    	}
    	void upd(int &id,int l,int r,int pos,int x){
    		if(!id) id=++tot;
    		if(l==r) {maxv[id]+=x;maxp[id]=l;return;}
    		if(pos<=mid) upd(ls[id],l,mid,pos,x);
    		else upd(rs[id],mid+1,r,pos,x);
    		pushup(id);
    	}
    	int merge(int x,int y,int l,int r){
    		if(!x||!y) return x+y;
    		if(l==r) {maxv[x]+=maxv[y];maxp[x]=l;return x;}
    		ls[x]=merge(ls[x],ls[y],l,mid);
    		rs[x]=merge(rs[x],rs[y],mid+1,r);
    		pushup(x);
    		return x;
    	}
    	#undef mid
    }trs;
    
    void predfs(int u,int rt){
    	fa[u][0]=rt;dep[u]=dep[rt]+1;
    	for(int i=head[u];i;i=nxt[i]){
    		if(to[i]==rt) continue;
    		predfs(to[i],u);
    	}
    }
    
    int getlca(int x,int y){
    	if(dep[x]>dep[y]) swap(x,y);
    	for(int i=20;i>=0;i--) if(dep[fa[y][i]]>=dep[x]) y=fa[y][i];
    	if(y==x) return x;
    	for(int i=20;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    	return fa[x][0];
    }
    
    void dfs(int u,int fa){
    	for(int i=head[u];i;i=nxt[i]){
    		if(to[i]==fa) continue;
    		dfs(to[i],u);
    		root[u]=trs.merge(root[u],root[to[i]],1,1e5);
    	}
    	if(trs.maxv[root[u]]==0) ans[u]=0;
    	else ans[u]=trs.maxp[root[u]];
    }
    
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1,u,v;i<n;i++){
    		scanf("%d%d",&u,&v);
    		add(u,v);add(v,u);
    	}
    	predfs(1,0);
    	for(int i=1;i<=20;i++)
    		for(int j=1;j<=n;j++)
    			fa[j][i]=fa[fa[j][i-1]][i-1];
    	for(int i=1,x,y,z;i<=m;i++){
    		scanf("%d%d%d",&x,&y,&z);
    		int lca=getlca(x,y);
    		trs.upd(root[x],1,1e5,z,1);
    		trs.upd(root[y],1,1e5,z,1);
    		trs.upd(root[lca],1,1e5,z,-1);
    		if(fa[lca][0]) trs.upd(root[fa[lca][0]],1,1e5,z,-1);
    	}
    	dfs(1,0);
    	for(int i=1;i<=n;i++) printf("%d
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    C++继承 派生类中的内存布局(单继承、多继承、虚拟继承)
    Linux 共享库(动态库)
    虚幻4
    从头认识java-16.5 nio的数据转换
    JavaScript实现禁用键盘和鼠标的点击事件
    Codeforces Round #277.5 (Div. 2)部分题解
    iOS-WKWebView使用
    我学cocos2d-x (三) Node:一切可视化对象的祖先
    Android Studio右下角不显示当前branch名称
    Neo4j简单的样例
  • 原文地址:https://www.cnblogs.com/BakaCirno/p/12671954.html
Copyright © 2011-2022 走看看