线段树分裂合并

权值线段树

——维护某个区间内数组元素出现的次数

权值线段树会采用离散化或动态开点的策略优化空间。单次操作时间复杂度O(logn)

权值线段树和简单线段树的区别

权值线段树维护的是桶,按值域开空间,维护的是个数

简单线段树维护的是信息,按个数可开空间,维护的是特定信息

板子——额可打平衡树板子https://www.luogu.com.cn/problem/P3369

可以解决数列第k大/小的问题——不是区间第k大

#include<cstdio>
#include<iostream>
#include<algorithm>
const int N=2000005;
const int INF = 10000005;
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int n,ls[N],rs[N],tot,cnt[N],root;
void add(int &p,int l,int r,int k,int d){
	if(!p) p=++tot;
	if(l==r) {cnt[p]+=d;return;}
	int mid=(l+r)>>1;
	if(k<=mid) add(ls[p],l,mid,k,d);
	else add(rs[p],mid+1,r,k,d);
	cnt[p]=cnt[ls[p]]+cnt[rs[p]];
}
int query(int p,int l,int r,int L,int R){
	if(!p) return 0;
	if(L<=l&&r<=R) {return cnt[p];}
	int mid=(l+r)>>1;
	int res=0;
	if(L<=mid) res+=query(ls[p],l,mid,L,R);
	if(R>mid) res+=query(rs[p],mid+1,r,L,R);
	return res; 
}
int kth(int p,int l,int r,int x){
	if(l==r) return l;//
	int mid=(l+r)>>1;
	if(cnt[ls[p]]>=x) return kth(ls[p],l,mid,x);
	else return kth(rs[p],mid+1,r,x-cnt[ls[p]]);
}
int get_rank(int p,int l,int r,int k){
	if(!p) return 0;
	if(l==r) return 1;
	int mid=(l+r)>>1;
	if(k<=mid) return get_rank(ls[p],l,mid,k);
	else return cnt[ls[p]]+get_rank(rs[p],mid+1,r,k);
}
int get_pre(int x){
	int rk=query(1,-INF,INF,-INF,x-1);
	return kth(1,-INF,INF,rk);
}
int get_nxt(int x){
	int rk=query(1,-INF,INF,-INF,x)+1;
	return kth(1,-INF,INF,rk);
}
int main() {
	n=read();
	for(int i=1,opt,x;i<=n;i++){
		opt=read();x=read();
		if(opt==1) add(root,-INF,INF,x,1);
		if(opt==2) add(root,-INF,INF,x,-1);
		if(opt==3) printf("%d\n",get_rank(1,-INF,INF,x));
		if(opt==4) printf("%d\n",kth(1,-INF,INF,x));
		if(opt==5) printf("%d\n",get_pre(x));
		if(opt==6) printf("%d\n",get_nxt(x));
	}
    return 0;
}

线段树合并

对于两棵线段树都有的节点,新的线段树的该节点值为两者和(按题意)。

对于某一棵线段树有的节点,新的线段树保存该节点的值。

然后对左右子树递归处理。

两个版本:

新建节点的 :

int merge(int x,int y,int l,int r) {
    if(!x||!y) return x|y;
    int p=++tree_cnt;
    if(l==r) {
        sum[p]=sum[x]+sum[y];
        按需要合并
        return p;
    } 
    int mid=(l+r)>>1;
    ls[p]=merge(ls[x],ls[y],l,mid);
    rs[p]=merge(rs[x],rs[y],mid+1,r);
    pushup(p);
    return p;
} 

不新建节点的:

int merge(int x,int y) {
    if(!x||!y) return x|y;
    val[x]+=val[y];//按需合并
    ls[x]=merge(ls[x],ls[y]);
    rs[x]=merge(rs[x],rs[y]);
    return x;
}

雨天的尾巴

首先树剖差分,我们每一个点开权值线段树,叶子结点记录每种有多少个,根结点合并上来就是记录最多的种类是哪个,最后dfs一边就好

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
inline int read() {
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int N=10000005;
const int M=200005;
int n,m,rt[N],ans[N];

int hd[M],nxt[M],to[M],tot;
inline void add(int x,int y) {
    to[++tot]=y;nxt[tot]=hd[x];hd[x]=tot;
}

int tree_cnt,ls[N],rs[N],sum[N],rev[N];
void pushup(int p) {//取 max
    if(sum[ls[p]]>=sum[rs[p]]) sum[p]=sum[ls[p]],rev[p]=rev[ls[p]];
    else sum[p]=sum[rs[p]],rev[p]=rev[rs[p]];
}

void modify(int l,int r,int pos,int val,int &p) {
    if(!p) p=++tree_cnt;
    if(l==r) {
        sum[p]+=val;rev[p]=l;
        return;
    }
    int mid=(l+r)>>1;
    if(pos<=mid) modify(l,mid,pos,val,ls[p]);
    else modify(mid+1,r,pos,val,rs[p]);
    pushup(p);
}

int merge(int x,int y,int l,int r) {
    if(!x||!y) return x|y;
    int p=++tree_cnt;
    if(l==r) {
        sum[p]=sum[x]+sum[y];
        rev[p]=l;
        return p;
    } 
    int mid=(l+r)>>1;
    ls[p]=merge(ls[x],ls[y],l,mid);
    rs[p]=merge(rs[x],rs[y],mid+1,r);
    pushup(p);
    return p;
} 

int siz[N],fa[N],dep[N],son[N];
void dfs_son(int x,int f) {
    siz[x]=1;fa[x]=f;dep[x]=dep[f]+1;
    for(int i=hd[x];i;i=nxt[i]) {
        int y=to[i];
        if(y==f) continue;
        dfs_son(y,x);
        siz[x]+=siz[y];
        if(siz[y]>siz[son[x]]) son[x]=y;
    }
}
int top[N],dfn[N],dfn_cnt;
void dfs_chain(int x,int tp) {
    dfn[x]=++dfn_cnt;top[x]=tp;
    if(son[x]) dfs_chain(son[x],tp);
    for(int i=hd[x];i;i=nxt[i]) {
        int y=to[i];
        if(dfn[y]) continue;
        dfs_chain(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(dfn[x]>dfn[y]) return y;
    else return x;
}

void dfs(int x) {
    for(int i=hd[x];i;i=nxt[i]) {
        int y=to[i];
        if(y==fa[x]) continue;
        dfs(y);
        rt[x]=merge(rt[x],rt[y],1,100000);
    }
    if(sum[rt[x]]) ans[x]=rev[rt[x]];
}

int main() {
    n=read();m=read();
    int x,y,z;
    for(int i=1;i<n;i++) {
        x=read();y=read();
        add(x,y);
        add(y,x);
    }
    dfs_son(1,0);dfs_chain(1,1);
    for(int i=1;i<=m;i++) {
        x=read();y=read();z=read();
        int lca=LCA(x,y);
        modify(1,100000,z,1,rt[x]);
        modify(1,100000,z,1,rt[y]);
        modify(1,100000,z,-1,rt[lca]);
        modify(1,100000,z,-1,rt[fa[lca]]);
    }
    dfs(1);
    for(int i=1;i<=n;i++) 
        printf("%d\n",ans[i]);
    return 0;
}

[POI2011]ROT-Tree Rotations

玄学读入见代码

对于每个叶子结点建一棵权值线段树,然后不断向上合并即可。

让求逆序对数最小,我们发现交换左右子树,左右子树内部逆序对不受影响,只有跨子树的受影响。

所以我们合并时分别统计交换的和不交换的,ans+=min(s1,s2)即可

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=10000010;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
int n,root,tree_cnt;
int ls[N],rs[N],rt[N];
ll s1,s2,ans;
ll siz[N];
bool vis[N];
void pushup(int p) {
    siz[p]=siz[ls[p]]+siz[rs[p]];
}

void modify(int L,int R,int pos,int &p) {
    if(!p) p=++tree_cnt;
    if(L==R) {
        siz[p]++;
        return;
    }
    int mid=(L+R)>>1;
    if(pos<=mid) modify(L,mid,pos,ls[p]);
    else modify(1+mid,R,pos,rs[p]);
    pushup(p);
}

int merge(int x,int y) {
    if(!x||!y) return x|y;
    s1+=siz[ls[x]]*siz[rs[y]];//change
    s2+=siz[ls[y]]*siz[rs[x]];//no change
    ls[x]=merge(ls[x],ls[y]);
    rs[x]=merge(rs[x],rs[y]);
    pushup(x);
    return x;
}

void dfs(int &x) {
    int val=read();
    x=++tree_cnt;
    if(!val) dfs(ls[x]),dfs(rs[x]);
    else vis[x]=1,modify(1,n,val,rt[x]);
}

void work(int x) {
    if(vis[x]) return;
    work(ls[x]),work(rs[x]);
    s1=s2=0;
    rt[x]=merge(rt[ls[x]],rt[rs[x]]);
    ans+=min(s1,s2);
}

int main() {
    n=read();
    dfs(root);
    work(root);
    printf("%lld\n",ans);
    return 0;
}

永无乡

注意:

并查集合并和线段树合并方向要一直(都是y向x合并)

modify记着&p,并且先val++在判return

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
inline int read() {
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-')f=-1; ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
const int N=5000005;
int n,m,q,tree_cnt;
int v[N],rev[N],rt[N],val[N];
int ls[N],rs[N];
int fa[N];
int find(int x) {
    return x==fa[x]?x:fa[x]=find(fa[x]);
}

void modify(int L,int R,int pos,int &p) {
    if(!p) p=++tree_cnt;
    val[p]++;
    if(L==R) return ;
    int mid=(L+R)>>1;
    if(pos<=mid) modify(L,mid,pos,ls[p]);
    else modify(mid+1,R,pos,rs[p]);
}

int merge(int x,int y) {
    if(!x||!y) return x|y;
    ls[x]=merge(ls[x],ls[y]);
    rs[x]=merge(rs[x],rs[y]);
    val[x]+=val[y];
    return x;
}

int query(int L,int R,int rnk,int p) {
    if(L==R) return L;
    int mid=(L+R)>>1;
    if(val[ls[p]]>=rnk) return query(L,mid,rnk,ls[p]);
    else return query(mid+1,R,rnk-val[ls[p]],rs[p]);
}
int main() {
    n=read();m=read();
    for(int i=1;i<=n;i++)
        v[i]=read(),rev[v[i]]=i,fa[i]=i;
    
    int x,y;
    for(int i=1;i<=m;i++) {
        x=read();y=read();
        fa[find(y)]=find(x);
    }
    for(int i=1;i<=n;i++)
        modify(1,n,v[i],rt[find(i)]);
    
    q=read();
    char op;
    while(q--) {
        cin>>op;
        x=read();y=read();
        if(op=='Q') {
            int fx=find(x);
            if(y>val[rt[fx]]) puts("-1");
            else printf("%d\n",rev[query(1,n,y,rt[fx])]);
        } else {
            int fx=find(x),fy=find(y);
            if(fx==fy) continue;
            fa[fy]=fx;
            rt[fx]=merge(rt[fx],rt[fy]);
        }
    }
    return 0;
}

CF600E Lomsat gelral

。。。。cf交不上

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
#define int long long
const int N=4e5+10;
vector<int>g[N];
int n,cnt,col[N],root[N];
int res[N];
int ls[N],rs[N],sum[N],ans[N];

void pushup(int p){
	if(sum[ls[p]]<sum[rs[p]]){
		sum[p]=sum[rs[p]];
		ans[p]=ans[rs[p]];
	}
	if(sum[ls[p]]>sum[rs[p]]){
		sum[p]=sum[ls[p]];
		ans[p]=ans[ls[p]];
	}
	if(sum[ls[p]]==sum[rs[p]]){
		sum[p]=sum[rs[p]];
		ans[p]=ans[ls[p]]+ans[rs[p]];
	}
}
void modify(int l,int r,int pos,int v,int &p) {
	if(!p) p=++cnt;
	if(l==r) {
		ans[p]=l;
		sum[p]+=v;
		return;
	}
	int mid=(l+r)>>1;
	if(pos<=mid) modify(l,mid,pos,v,ls[p]);
	else modify(mid+1,r,pos,v,rs[p]);
	pushup(p);
}
int merge(int a,int b,int l,int r){
	if(!a) return b;
	if(!b) return a;
	if(l==r) {
		ans[a]=l;
		sum[a]+=sum[b];
		return a;
	}
	int mid=(l+r)>>1;
	ls[a]=merge(ls[a],ls[b],l,mid);
	rs[a]=merge(rs[a],rs[b],mid+1,r);
	pushup(a);
	return a;
}
void dfs(int x,int f){
	int siz=g[x].size();
	for(int i=0;i<siz;i++){
		int y=g[x][i];
		if(y==f) continue;
		dfs(y,x);
		root[x]=merge(root[x],root[y],1,n);
	}
	modify(1,n,col[x],1,root[x]);
	res[x]=ans[root[x]];
}
signed main(){
	scanf("%lld",&n);
	cnt=n;
	for(int i=1;i<=n;i++)
		scanf("%lld",&col[i]);
	for(int i=1,x,y;i<n;i++){
		scanf("%lld%lld",&x,&y);
		g[x].push_back(y);
		g[y].push_back(x);
	}
	dfs(1,0);
	for(int i=1;i<=n;i++)
		printf("%lld ",res[i]);
	return 0;
}

题:

https://blog.csdn.net/qq_35811706/article/details/84726342

线段树分裂

模板

void split(int l,int r,int L,int R,int &x,int &y) {
    if(!x) return;
    if(L<=l&&r<=R) {
        y=x;x=0;
        return;
    }
    y=++tree_cnt;
    int mid=(l+r)>>1;
    if(L<=mid) split(l,mid,L,R,ls[x],ls[y]);
    if(R>mid) split(mid+1,r,L,R,rs[x],rs[y]);
    pushup(x),pushup(y);
}
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
inline int read() {
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
typedef long long ll;
const int N=10000005;
int n,m,num=1;
int ls[N],rs[N],rt[N],tree_cnt;
ll sum[N];

void pushup(int p) {
    sum[p]=sum[ls[p]]+sum[rs[p]];
}
void modify(int l,int r,int pos,ll v,int &p) {
    if(!p) p=++tree_cnt;
    if(l==r) {
        sum[p]+=v;
        return;
    }
    int mid=(l+r)>>1;
    if(pos<=mid) modify(l,mid,pos,v,ls[p]);
    else modify(mid+1,r,pos,v,rs[p]);
    pushup(p);
}
ll query(int l,int r,int L,int R,int p) {
    if(!p) return 0;
    if(L<=l&&r<=R) return sum[p];
    ll res=0;
    int mid=(l+r)>>1;
    if(L<=mid) res+=query(l,mid,L,R,ls[p]);
    if(R>mid) res+=query(mid+1,r,L,R,rs[p]);
    return res;
}
int merge(int x,int y) {
    if(!x||!y) return x|y;
    int p=++tree_cnt;
    sum[p]=sum[x]+sum[y];
    ls[p]=merge(ls[x],ls[y]);
    rs[p]=merge(rs[x],rs[y]);
    return p;
}
int kth(int l,int r,ll k,int p) {
    if(l==r) return l;
    int mid=(l+r)>>1;
    if(sum[ls[p]]>=k) return kth(l,mid,k,ls[p]);
    else return kth(mid+1,r,k-sum[ls[p]],rs[p]);
}
void split(int l,int r,int L,int R,int &x,int &y) {
    if(!x) return;
    if(L<=l&&r<=R) {
        y=x;x=0;
        return;
    }
    y=++tree_cnt;
    int mid=(l+r)>>1;
    if(L<=mid) split(l,mid,L,R,ls[x],ls[y]);
    if(R>mid) split(mid+1,r,L,R,rs[x],rs[y]);
    pushup(x),pushup(y);
}
int main() {
    n=read();m=read();
    int x;
    for(int i=1;i<=n;i++) {
        x=read();
        modify(1,n,i,x,rt[1]);
    }
    int opt,a,y,b;
    for(int i=1;i<=m;i++) {
        opt=read();a=read();
        if(opt==0) {
            x=read();y=read();
            split(1,n,x,y,rt[a],rt[++num]);
        } else if(opt==1) {
            b=read();
            rt[a]=merge(rt[a],rt[b]);
        } else if(opt==2) {
            x=read();y=read();
            modify(1,n,y,x,rt[a]);
        } else if(opt==3) {
            x=read();y=read();
            printf("%lld\n",query(1,n,x,y,rt[a]));
        } else {
            b=read();
            if(sum[rt[a]]<b) puts("-1");
            else printf("%d\n",kth(1,n,b,rt[a]));
        }
    }
    return 0;
}
posted @ 2020-08-14 00:23  ke_xin  阅读(34)  评论(0编辑  收藏  举报