线段树分裂合并
权值线段树
——维护某个区间内数组元素出现的次数
权值线段树会采用离散化或动态开点的策略优化空间。单次操作时间复杂度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;
}