**~~
线段树合并总结
**~~
**本来是打算补套数据结构的SAM的结果看到一个题要用这个东西。什么玩意儿我怎么没见过 就先补一下
大概内容就是对每个点开一颗权值线段树(根据情况可能会维护除了siz外的其他信息),需要的时候合并两个节点的树维护的信息。具体用途还是看题吧。
单次操作的复杂度o(logn)
模板
struct node{
int ls,rs,siz;//维护的内容与pushup等函数要按具体情况修改
}tr[log(MAX_N)*add调用次数];
int tot=0,rt[MAX_N];
inline void pushup(int k)
{
tr[k].siz=tr[tr[k].ls].siz+tr[tr[k].rs].siz;
}
void add(int &k,int l,int r,int val)
{
if(!k) k=++tot;
if(l==r){tr[k].siz+=val;return ;}
int mid=(l+r)>>1;
if(pos<=mid) add(tr[k].ls,l,mid,pos,val);
else add(tr[k].rs,mid+1,r,pos,val);
pushup(k);
}
void merge(int l,int r,int &p,int q)
{
if(!p||!q){p=(!p)?q:p;return ;}
if(l==r){tr[p].siz+=tr[q].siz; return;}
int mid=(l+r)>>1;
merge(l,mid,tr[p].ls,tr[q].ls);
merge(mid+1,r,tr[p].rs,tr[q].rs);
pushup(p);
}
例题:
1.P3521 给一颗有N个叶子的树,可以交换每个节点的左右子树,问前序遍历情况下逆序对最少几个
对于一棵树,逆序对存在3种情况-都在左树,都在右树,左右交叉,交换左右子树只影响第三种
故对第三种取最小值即可(不交换的情况左子树的右子树大小*右子树的左子树大小,交换则相反),对于左右子树同在左/右侧的情况,会在向下合并区间时更新。边合并边更新。
const int MAX_N=2e5+5;
struct node{
int ls,rs,siz;
}tr[MAX_N*22];//每个叶节点实际上建的是一条链,用到logn个节点,故共开nlogn个节点
int n,tot=0;
ll ans=0,u,v;
int update(int l,int r,int val)//对每个叶节点建权值线段树
{
int k=++tot;
tr[k].siz++;
if(l==r) return k;
int mid=(l+r)>>1;
if(val<=mid) tr[k].ls=update(l,mid,val);
else tr[k].rs=update(mid+1,r,val);
return k;
}
int merge(int l,int r,int p,int q)
{
if(!p||!q) return (!p)?q:p;
if(l==r){
tr[p].siz+=tr[q].siz; return p;
}
u+=1ll*tr[tr[p].rs].siz*tr[tr[q].ls].siz//不交换
v+=1ll*tr[tr[p].ls].siz*tr[tr[q].rs].siz;//交换
int mid=(l+r)>>1;
tr[p].ls=merge(l,mid,tr[p].ls,tr[q].ls);//对于同侧的会在合并时计算
tr[p].rs=merge(mid+1,r,tr[p].rs,tr[q].rs);
tr[p].siz=tr[tr[p].ls].siz+tr[tr[p].rs].siz;
return p;
}
int dfs()
{
int pos,val; si(val);
if(!val){
int ls=dfs(),rs=dfs();
u=v=0;
pos=merge(1,n,ls,rs);
ans+=min(u,v);
}
else pos=update(1,n,val);//叶子节点,建树
return pos;
}
int main()
{
si(n);
dfs();
printf("%lld\n",ans);
}
2.P3224 n个点,每个点重要度不同(名次1-n与序号不同),开始的时候一些点间连了边。有路径的两点称为连通。给出两种操作B x y x与y连边 Q x k 询问x所在连通块重要度排名第k小是哪座岛
并查集+线段树合并 注意并查集合并y并给x的时候也是把y树合到x树上以及每次操作都是对根进行的 对于第K小的查询为权值线段树的基本应用,此处略过
const int MAX_N=1e5+5;
struct Union_Find{
int fa[MAX_N];
void init(int n){repi(i,0,n)fa[i]=i;}
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
}uf;
int n,m;
int a[MAX_N],rt[MAX_N],id[MAX_N];
struct node{
int ls,rs,siz;
}tr[MAX_N*22];
int tot=0;
int add(int l,int r,int val)
{
int k=++tot; tr[k].siz++;
if(l==r) return k;
int mid=(l+r)>>1;
if(val<=mid) tr[k].ls=add(l,mid,val);
else tr[k].rs=add(mid+1,r,val);
return k;
}
int merge(int l,int r,int p,int q)
{
if(!p||!q) return (!p)?q:p;
if(l==r){
tr[p].siz+=tr[q].siz; return p;
}
int mid=(l+r)>>1;
tr[p].ls=merge(l,mid,tr[p].ls,tr[q].ls);
tr[p].rs=merge(mid+1,r,tr[p].rs,tr[q].rs);
tr[p].siz=tr[tr[p].ls].siz+tr[tr[p].rs].siz;
return p;
}
int query(int k,int l,int r,int kth)
{
if(l==r) return l;
int mid=(l+r)>>1;
if(kth<=tr[tr[k].ls].siz) return query(tr[k].ls,l,mid,kth);
else return query(tr[k].rs,mid+1,r,kth-tr[tr[k].ls].siz);
}
int main()
{
si(n),si(m);
uf.init(n);
repi(i,1,n) si(a[i]),rt[i]=add(1,n,a[i]),id[a[i]]=i;
repi(i,1,m){
int x,y; si(x),si(y); x=uf.find(x),y=uf.find(y);
if(x!=y) uf.fa[y]=x,rt[x]=merge(1,n,rt[x],rt[y]);
}
int q; si(q);
while(q--){
char s[10]; ss(s+1);
int x,y; si(x),si(y);
if(s[1]=='B'){
x=uf.find(x),y=uf.find(y);
if(x!=y) uf.fa[y]=x,rt[x]=merge(1,n,rt[x],rt[y]);
}
else{
x=uf.find(x);
int ans;
if(tr[rt[x]].siz<y) ans=-1;
else ans=id[query(rt[x],1,n,y)];
printf("%d\n",ans);
}
}
return 0;
}
3.P3605 n个点,每个点一个权值,问以每个点为根的子树中有几个点权值大于根权值
对权值先进行离散化处理,由叶子向上进行线段树合并即可。
const int MAX_N=1e5+5;
struct Edge{
int to,nxt;
}e[MAX_N<<1];
int head[MAX_N],tote;
void add_edge(int u,int v)
{
e[++tote].to=v,e[tote].nxt=head[u];
head[u]=tote;
}
void init(int n)
{
repi(i,0,n) head[i]=0;
tote=0;
}
int n,a[MAX_N];
int rt[MAX_N];
vector<int> h;
int cnt;
struct node{
int ls,rs,siz;
}tr[MAX_N*22];
int tot=0;
int add(int l,int r,int val)
{
int k=++tot; tr[k].siz++;
if(l==r) return k;
int mid=(l+r)>>1;
if(val<=mid) tr[k].ls=add(l,mid,val);
else tr[k].rs=add(mid+1,r,val);
return k;
}
int merge(int l,int r,int p,int q)
{
if(!p||!q) return (!p)?q:p;
if(l==r){
tr[p].siz+=tr[q].siz; return p;
}
int mid=(l+r)>>1;
tr[p].ls=merge(l,mid,tr[p].ls,tr[q].ls);
tr[p].rs=merge(mid+1,r,tr[p].rs,tr[q].rs);
tr[p].siz=tr[tr[p].ls].siz+tr[tr[p].rs].siz;
return p;
}
int query(int k,int l,int r,int val)
{
if(l==r) return tr[k].siz;
int mid=(l+r)>>1;
if(val<=mid) return query(tr[k].ls,l,mid,val)+tr[tr[k].rs].siz;
else return query(tr[k].rs,mid+1,r,val);
}
int ans[MAX_N];
void dfs(int s,int fa){
reps(s)if(e[i].to!=fa){
dfs(e[i].to,s);
rt[s]=merge(1,cnt,rt[s],rt[e[i].to]);
}
ans[s]=query(rt[s],1,cnt,a[s]+1);
}
int main()
{
si(n); h.pb(-1);
init(n);
repi(i,1,n) si(a[i]),h.pb(a[i]);
sort(all(h)),h.erase(unique(all(h)),h.end());
cnt=h.size();
repi(i,1,n) a[i]=lower_bound(all(h),a[i])-h.begin(),rt[i]=add(1,cnt,a[i]);
repi(i,2,n){
int fa; si(fa);
add_edge(i,fa),add_edge(fa,i);
}
dfs(1,1);
repi(i,1,n) printf("%d\n",ans[i]);
return 0;
}
4.P4556 n个点,树形结构,每次x到y的路径每个点发一袋z种粮食,问最后每个点哪种粮食最多(相等的取编号小的)
树上差分处理每次操作 x y节点+1 lca(x,y)和fa[lca(x,y)]-1,由下向上合并即可
const int MAX_N=1e5+5;
struct Edge{
int to,nxt;
}e[MAX_N<<1];
int head[MAX_N],tote;
void add_edge(int u,int v)
{
e[++tote].to=v,e[tote].nxt=head[u];
head[u]=tote;
}
int fa[MAX_N],dep[MAX_N],siz[MAX_N],top[MAX_N],son[MAX_N];
int idx,dfn[MAX_N],rdfn[MAX_N];
void dfs_1(int u,int pre)
{
dep[u]=dep[pre]+1,fa[u]=pre,siz[u]=1;
reps(u)if(e[i].to!=pre){
int v=e[i].to;
dfs_1(v,u);
siz[u]+=siz[v];
if(son[u]==-1||siz[son[u]]<siz[v]) son[u]=v;
}
}
void dfs_2(int u,int tp)
{
top[u]=tp,dfn[u]=++idx,rdfn[dfn[u]]=u;
if(son[u]==-1) return;
dfs_2(son[u],tp);
reps(u){
int v=e[i].to;
if(v!=fa[u]&&v!=son[u]) dfs_2(v,v);
}
}
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]];
}
return dep[x]<dep[y]?x:y;
}
int n,m;
int x[MAX_N],y[MAX_N],z[MAX_N],mx=0;
struct node{
int ls,rs,siz,mn;
}tr[MAX_N*60];
int tot=0,rt[MAX_N];
inline void pushup(int k)
{
int pos=((tr[tr[k].ls].siz>=tr[tr[k].rs].siz)?tr[k].ls:tr[k].rs);
tr[k].siz=tr[pos].siz,tr[k].mn=tr[pos].mn;
}
void add(int &k,int l,int r,int pos,int val)
{
if(!k) k=++tot;
if(l==r){
tr[k].siz+=val,tr[k].mn=l; return ;
}
int mid=(l+r)>>1;
if(pos<=mid) add(tr[k].ls,l,mid,pos,val);
else add(tr[k].rs,mid+1,r,pos,val);
pushup(k);
}
void merge(int l,int r,int &p,int q)
{
if(!p||!q){p=(!p)?q:p;return ;}
if(l==r){
tr[p].siz+=tr[q].siz,tr[p].mn=l; return;
}
int mid=(l+r)>>1;
merge(l,mid,tr[p].ls,tr[q].ls);
merge(mid+1,r,tr[p].rs,tr[q].rs);
pushup(p);
}
int ans[MAX_N];
void dfs_3(int s,int pre)
{
reps(s)if(e[i].to!=pre) dfs_3(e[i].to,s),merge(1,mx,rt[s],rt[e[i].to]);
if(tr[rt[s]].siz) ans[s]=tr[rt[s]].mn;
}
void init(int n)
{
repi(i,0,n) head[i]=0,son[i]=-1;
tote=idx=dep[0]=0;
}
int main()
{
si(n),si(m); init(n);
repi(i,1,n-1){
int u,v; si(u),si(v);
add_edge(u,v),add_edge(v,u);
}
dfs_1(1,0),dfs_2(1,1);
repi(i,1,m) si(x[i]),si(y[i]),si(z[i]),mx=max(mx,z[i]);
repi(i,1,m){
int lca=LCA(x[i],y[i]);
add(rt[x[i]],1,mx,z[i],1),add(rt[y[i]],1,mx,z[i],1);
add(rt[lca],1,mx,z[i],-1);
if(fa[lca]) add(rt[fa[lca]],1,mx,z[i],-1);
}
dfs_3(1,0);
repi(i,1,n) printf("%d\n",ans[i]);
return 0;
}
暂且先记录到这,好像还有什么线段树分裂/分治 一块补掉再记录吧
坑越挖越多