zoukankan      html  css  js  c++  java
  • CF600E Lomsat gelral(线段树合并)

    http://codeforces.com/problemset/problem/600/E

    题意:给一个树,每个点有一个颜色,让你对于每个点,求以他为根的子树中,颜色是 出现数量最多的颜色 的节点,的编号和(如果有多个出现数量最多的颜色,都算),(nle 10^5)

    线段树合并

    用到线段树合并,线段树合并大概就是将两颗线段树的信息合并到一个上
    这里合并的大都是动态开点的线段树,因为如果要合并一颗满的线段树(就是每个节点都有),那直接 (O(n)) 新建一颗树大概就是最好的复杂度了
    但如果是权值线段树等需要动态开点的,点的范围一般会非常大,(O(n)) 新建显然是不可能,所以下面说的这种合并是 (O( ext{两颗要合并的树的重叠大小}))
    考虑如何把 (b) 合并到 (a)

    • 如果 (a)(b) 是空的,直接返回另一个
    • 如果 (a)(b) 已经是叶子节点了,根据题目的具体需要把 (b) 的信息加到 (a) 上,返回 (a)
    • 递归 (a,b) 的左儿子和右儿子进行合并
    • 合并 (a) 左右儿子的信息到 (a),也就是 pushup

    正确性应该比较显然

    关于复杂度:
    从上面的步骤可以发现单次合并的复杂度是 (O( ext{两颗要合并的树的重叠大小})),不是 (O(log n))
    那如果要合并 (n) 个都只有一个元素的线段树为一个有 (n) 个元素的线段树呢?应该是 (O(nlog n)),因为一个树一个树合并时,都只有一个元素,重叠部分就是一个 (O(log n)) 的链
    所以其实当线段树值域大,但因为是动态开点,所以元素不多时,这种合并方式复杂度还是很优的

    此题

    对每个节点来一个动态开点的线段树,维护颜色,然后把每个节点的儿子的线段树都合并到它自己,这样此时他的线段树维护的就是整个子树的颜色信息了
    线段树结构体中,cnt 是最多的颜色的出现次数,ans 是答案(编号和)
    至于怎么 pushup 是线段树基本操作了(

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<map>
    #include<iomanip>
    #include<cstring>
    #define reg register
    #define EN puts("")
    inline int read(){
    	register int x=0;register int y=1;
    	register char c=std::getchar();
    	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
    	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
    	return y?x:-x;
    }
    #define N 100006
    #define M 200006
    struct graph{
    	int fir[N],nex[M],to[M],tot;
    	inline void add(int u,int v){
    		to[++tot]=v;
    		nex[tot]=fir[u];fir[u]=tot;
    	}
    }G;
    int n;
    int color[N];
    struct Node{
    	Node *ls,*rs;
    	int color,cnt;
    	long long ans;
    }dizhi[N*60],*root[N],*null=&dizhi[0];
    int tot;
    long long ans[N];
    inline void New(Node *&a){
    	a=&dizhi[++tot];a->ls=a->rs=null;
    }
    inline void pushup(Node *tree){
    	if(tree->ls->cnt^tree->rs->cnt){
    		Node *tmp=tree->ls->cnt>tree->rs->cnt?tree->ls:tree->rs;
    		tree->cnt=tmp->cnt;
    		tree->color=tmp->color;
    		tree->ans=tmp->ans;
    	}
    	else{
    		tree->cnt=tree->ls->cnt;
    		tree->color=tree->ls->color;
    		tree->ans=tree->ls->ans+tree->rs->ans;
    	}
    }
    void change(Node *tree,int l,int r,int pos){
    	if(l==r){
    		tree->color=l;
    		tree->cnt++;tree->ans=l;
    		return;
    	}
    	int mid=(l+r)>>1;
    	if(pos<=mid){
    		if(tree->ls==null) New(tree->ls);
    		change(tree->ls,l,mid,pos);
    	}
    	else{
    		if(tree->rs==null) New(tree->rs);
    		change(tree->rs,mid+1,r,pos);
    	}
    	pushup(tree);
    }
    Node *merge(Node *a,Node *b,int l,int r){
    	if(a==null) return b;
    	if(b==null) return a;
    	if(l==r){
    		a->color=l;a->cnt+=b->cnt;
    		a->ans=l;
    		return a;
    	}
    	int mid=(l+r)>>1;
    	a->ls=merge(a->ls,b->ls,l,mid);
    	a->rs=merge(a->rs,b->rs,mid+1,r);
    	pushup(a);
    	return a;
    }
    void dfs(reg int u,int fa){
    	for(reg int v,i=G.fir[u];i;i=G.nex[i]){
    		v=G.to[i];
    		if(v==fa) continue;
    		dfs(v,u);
    		merge(root[u],root[v],1,1e5);
    	}
    	change(root[u],1,1e5,color[u]);
    	ans[u]=root[u]->ans;
    }
    int main(){
    	n=read();
    	for(reg int i=1;i<=n;i++) color[i]=read(),New(root[i]);
    	for(reg int u,v,i=1;i<n;i++){
    		u=read();v=read();
    		G.add(u,v);G.add(v,u);
    	}
    	dfs(1,1);
    	for(reg int i=1;i<=n;i++) printf("%lld ",ans[i]);
    	return 0;
    }
    

    其他的线段树合并:https://www.luogu.com.cn/training/3858

  • 相关阅读:
    eclipse + maven 环境配置
    腾讯管家去除桌面快捷小图标
    C# 在同一个项目里启动不同的类文件
    面试题-数据库篇
    面试题-编程篇
    DevExpress控件-GridControl根据条件改变单元格(Dev GridControl 单元格着色)
    Developer Express控件gridcontrol中gridView的某一个单元格是否可以自由输入
    oracle11g如何创建数据库
    通过第三方组件NPOI读取Excel的方法
    Oracle11g常用的命令
  • 原文地址:https://www.cnblogs.com/suxxsfe/p/13839555.html
Copyright © 2011-2022 走看看