zoukankan      html  css  js  c++  java
  • [学习笔记]线段树合并

    1、[POI2011]ROT-Tree Rotations

    分析:线段树合并人生第一题。

    网上的题解我都没看懂……我自己讲一下好了

    线段树合并就是把两棵权值线段树合并到一棵

    那怎么合并呢?

    假设有这么两棵树:

    一个结点代表一段值域区间有几个数,那么可以看出合并后应该是这样的

    然后具体步骤就是找到一个结点,如果一个结点一棵树上有一棵树上没有,那么直
    接返回那个结点的编号,否则两个值域的和相加,递归至左儿子和右儿子

    每次合并的复杂度为两棵线段树的点数相加

    这道题可以算出左儿子的逆序对个数,右儿子的逆序对个数,然后求出 ((x,y)) 的对数, $xin $ 左儿子, $yin $ 右儿子

    每次记录一下 (sum),在每次合并的时候 (sum[lson[x]] imes sum[rson[y]])(sum[rson[x]] imes sum[lson[y]]) 中取个最小值算进答案的贡献就可以了(不懂可以自己画图或看代码理解一下)

    (Code Below:)

    #include <bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int maxn=200000+10;
    int n,L[maxn*24],R[maxn*24],sum[maxn*24],cnt;
    ll ans,num1,num2;
    
    inline int read(){
    	register int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    	return (f==1)?x:-x;
    }
    
    void update(int &now,int l,int r,int x){
    	if(!now) now=++cnt;
    	sum[now]++;
    	if(l == r) return ;
    	int mid=l+r>>1;
    	if(x <= mid) update(L[now],l,mid,x);
    	else update(R[now],mid+1,r,x);
    }
    
    void merge(int &x,int y){
    	if(!x||!y){
    		x=x+y;
    		return ;
    	}
    	sum[x]+=sum[y];
    	num1+=(ll)sum[R[x]]*sum[L[y]];
    	num2+=(ll)sum[L[x]]*sum[R[y]];
    	merge(L[x],L[y]);
    	merge(R[x],R[y]);
    }
    
    void dfs(int &x){
    	int val,lson=0,rson=0;
    	val=read();
    	if(!val){
    		dfs(lson);dfs(rson);
    		num1=num2=0;x=lson;
    		merge(x,rson);
    		ans+=min(num1,num2);
    	}
    	else update(x,1,n,val);
    }
    
    int main()
    {
    	n=read();
    	int x=0;
    	dfs(x);
    	printf("%lld
    ",ans);	
    	return 0;
    }
    

    2、[Vani有约会]雨天的尾巴

    分析:考虑树上查分。这次权值线段树维护最多救济粮的编号 (val) 和 最多救济粮的个数 (sum),在叶子节点更新一下,最后 (pushup) 一下

    (Code Below:)

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=100000+10;
    const int size=100000;
    int n,m,ans[maxn],rt[maxn],L[maxn*80],R[maxn*80],val[maxn*80],sum[maxn*80],cnt;
    int head[maxn],to[maxn<<1],nxt[maxn<<1],tot;
    int top[maxn],dep[maxn],siz[maxn],son[maxn],fa[maxn];
    
    inline int read(){
        register int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
        return (f==1)?x:-x;
    }
    inline void add(int x,int y){
    	to[++tot]=y;
    	nxt[tot]=head[x];
    	head[x]=tot;
    }
    
    void dfs1(int x,int f){
    	siz[x]=1;fa[x]=f;
    	dep[x]=dep[f]+1;
    	int maxson=-1;
    	for(int i=head[x],y;i;i=nxt[i]){
    		y=to[i];
    		if(y==f) continue;
    		dfs1(y,x);
    		siz[x]+=siz[y];
    		if(maxson<siz[y]){
    			maxson=siz[y];
    			son[x]=y;
    		}
    	}
    }
    
    void dfs2(int x,int topf){
    	top[x]=topf;
    	if(son[x]) dfs2(son[x],topf);
    	for(int i=head[x],y;i;i=nxt[i]){
    		y=to[i];
    		if(y==fa[x]||y==son[x]) continue;
    		dfs2(y,y);
    	}
    }
    
    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]];
    	}
    	if(dep[x]>dep[y]) swap(x,y);
    	return x;
    }
    
    void pushup(int now){
    	if(sum[L[now]]>sum[R[now]]) sum[now]=sum[L[now]],val[now]=val[L[now]];
    	else if(sum[L[now]]<sum[R[now]]) sum[now]=sum[R[now]],val[now]=val[R[now]];
    	else sum[now]=sum[L[now]],val[now]=min(val[L[now]],val[R[now]]);
    }
    
    void update(int &now,int l,int r,int x,int v){
    	if(!now) now=++cnt;
    	if(l == r){sum[now]+=v;val[now]=sum[now]?l:0;return ;}
    	int mid=l+r>>1;
    	if(x<=mid) update(L[now],l,mid,x,v);
    	else update(R[now],mid+1,r,x,v);
    	pushup(now);
    }
    
    void merge(int &x,int y,int l,int r){
    	if(!x||!y){x=x+y;return ;}
    	if(l == r){sum[x]+=sum[y];val[x]=sum[x]?l:0;return ;}
    	int mid=l+r>>1;
    	merge(L[x],L[y],l,mid);
    	merge(R[x],R[y],mid+1,r);
    	pushup(x);
    }
    
    void dfs(int x,int f){
    	for(int i=head[x],y;i;i=nxt[i]){
    		y=to[i];
    		if(y==f) continue;
    		dfs(y,x);
    		merge(rt[x],rt[y],1,size);
    	}
    	ans[x]=val[rt[x]];
    }
    
    int main()
    {
    	n=read(),m=read();
    	int x,y,z,lca;
    	for(int i=1;i<n;i++){
    		x=read(),y=read();
    		add(x,y);add(y,x);
    	}
    	dfs1(1,0);dfs2(1,1);
    	for(int i=1;i<=m;i++){
    		x=read(),y=read(),z=read();
    		lca=LCA(x,y);
    		update(rt[x],1,size,z,1);
    		update(rt[y],1,size,z,1);
    		update(rt[lca],1,size,z,-1);
    		if(fa[lca]) update(rt[fa[lca]],1,size,z,-1);
    	}
    	dfs(1,0);
    	for(int i=1;i<=n;i++)
    		printf("%d
    ",ans[i]);
        return 0;
    }
    

    3、CF600E Lomsat gelral

    分析:这次再维护一个 (val) 最大的和 (ans),从下向上合并即可

    (Code Below:)

    #include <bits/stdc++.h>
    #define mid (l+r>>1)
    #define lson L[now]
    #define rson R[now]
    #define int long long
    using namespace std;
    const int maxn=100000+10;
    const int size=100000;
    int n,m,a[maxn],an[maxn],rt[maxn],cnt;
    int L[maxn*20],R[maxn*20],sum[maxn*20],val[maxn*20],ans[maxn*20];
    int head[maxn],to[maxn<<1],nxt[maxn<<1],tot;
    
    inline int read(){
        register int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
        return (f==1)?x:-x;
    }
    inline void add(int x,int y){
    	to[++tot]=y;
    	nxt[tot]=head[x];
    	head[x]=tot;
    }
    
    void pushup(int now){
    	sum[now]=(sum[lson]>sum[rson])?sum[lson]:sum[rson];
    	val[now]=(sum[lson]>sum[rson])?val[lson]:val[rson];
    	ans[now]=((sum[lson]>=sum[rson])?ans[lson]:0)+((sum[lson]<=sum[rson])?ans[rson]:0);
    }
    
    void update(int &now,int l,int r,int x){
    	if(!now) now=++cnt;
    	if(l == r){sum[now]++;val[now]=ans[now]=l;return ;}
    	if(x <= mid) update(lson,l,mid,x);
    	else update(rson,mid+1,r,x);
    	pushup(now);
    }
    
    void merge(int &x,int y,int l,int r){
    	if(!x||!y){x=x+y;return ;}
    	if(l == r){sum[x]+=sum[y];val[x]=ans[x]=l;return ;}
    	merge(L[x],L[y],l,mid);
    	merge(R[x],R[y],mid+1,r);
    	pushup(x);
    }
    
    void dfs(int x,int f){
    	for(int i=head[x],y;i;i=nxt[i]){
    		y=to[i];
    		if(y==f) continue;
    		dfs(y,x);
    		merge(rt[x],rt[y],1,size);
    	}
    	an[x]=ans[rt[x]];
    }
    
    signed main()
    {
    	n=read();
    	int x,y;
    	for(int i=1;i<=n;i++) a[i]=read();
    	for(int i=1;i<n;i++){
    		x=read(),y=read();
    		add(x,y);add(y,x);
    	}
    	for(int i=1;i<=n;i++)
    		update(rt[i],1,size,a[i]);
    	dfs(1,0);
    	for(int i=1;i<=n;i++)
    		printf("%lld ",an[i]);
    	printf("
    ");
        return 0;
    }
    

    搞了一个下午线段树合并,还是很有成就感滴!

  • 相关阅读:
    ACM-ICPC 2018 徐州赛区网络预赛 J. Maze Designer (最大生成树+LCA)
    ACM-ICPC 2018 沈阳赛区网络预赛 F. Fantastic Graph (上下界网络流)
    ACM-ICPC 2018 沈阳赛区网络预赛 G. Spare Tire (容斥原理)
    HDU 3081 Marriage Match II (二分+并查集+最大流)
    ISAP模板
    POJ
    青春
    登高有感
    那年今日
    NOIP200101数的计算
  • 原文地址:https://www.cnblogs.com/owencodeisking/p/9965525.html
Copyright © 2011-2022 走看看