线段树合并
一日双更了属于是,后续可能会把二分,分裂和分治也写到这里来。
前置:动态开点权值线段树。
在学完了dsu on tree 之后,我们就能在题解区发现线段树合并算法了(雾)。线段树合并,顾名思义也是线段树上的一种操作,其实他的内容就一个函数,很好理解。
我们考虑现在有两颗动态开点线段树,我们要合并他们的信息,这个信息可能是什么
一个基本模版。
inline void merge(ll &rt1,ll rt2,ll l,ll r)
{
if(!rt1) return rt1=rt2,void();if(!rt2) return;
if(l==r){/*do something*/return;}
ll mid=l+r>>1;
merge(tree[rt1].l,tree[rt2].l,l,mid);
merge(tree[rt1].r,tree[rt2].r,mid+1,r);
pushup(rt1);
}
此处是将 rt2 合并到 rt1 这颗树上,还是很好理解的,这种写法是遍利到叶子节点再回溯,通过 pushup 来更新答案的。
线段树合并的复杂度是
第一个操作很显然可以用并查集 + 线段树合并完成,第二个操作就是线段树上二分,就做完了?
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3f
#define inf 0x3f3f3f3f
#define pr putchar('\n')
#define fi first
#define se second
#define pp putchar(' ')
#define pii pair<ll,ll>
#define pdi pair<ll,ll>
#define mem(aa,bb) memset(aa,bb,sizeof(aa))
#define fo(a,i,b) for(register ll i = a ; i <= b ; ++ i )
#define Fo(a,i,b) for(register ll i = a ; i >= b ; -- i )
#define pb push_back
//#pragma GCC optimize(2)
using namespace std;
//typedef int ll;
typedef long long ll;
//typedef __int128 ll;
typedef double db;
inline void read(ll &opp){ll x=0,t=1;char ch;ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-'){t=-1;}ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}opp=x*t;return; }
inline void wr(ll x){if(x<0){putchar('-');x=-x;}if(x>9){wr(x/10);}putchar(x%10+'0');}
const ll N=1e6+5,M=2e4+5,mod=1e9+7;
ll n,m,p[N],rot[N],cnt,fa[N],q,id;
struct SGT{ll l,r,sum,cnt;}tree[N<<2];
#define rt tree[root]
#define ls tree[tree[root].l]
#define rs tree[tree[root].r]
inline void pushup(ll root){rt.cnt=ls.cnt+rs.cnt;}
inline void upd(ll &root,ll l,ll r,ll x,ll k)
{
if(!root) root=++cnt;
if(l==r) return rt.cnt=1,rt.sum=k,void();
ll mid=l+r>>1;if(x<=mid) upd(rt.l,l,mid,x,k);else upd(rt.r,mid+1,r,x,k);
pushup(root);
}
inline void merge(ll &t1,ll t2,ll l,ll r)
{
if(!t1) return t1=t2,void();if(!t2) return;
if(l==r)
{
if(tree[t2].sum) tree[t1].sum=tree[t2].sum;
tree[t1].cnt+=tree[t2].cnt;
return;
}
ll mid=l+r>>1;
merge(tree[t1].l,tree[t2].l,l,mid);
merge(tree[t1].r,tree[t2].r,mid+1,r);
pushup(t1);
}
inline ll ask(ll root,ll l,ll r,ll k)
{
if(rt.cnt<k) return -1;
if(l==r) return rt.sum;
ll mid=l+r>>1;
if(ls.cnt>=k) return ask(rt.l,l,mid,k);
else return ask(rt.r,mid+1,r,k-ls.cnt);
}
inline ll Find(ll x){return fa[x]==x?x:fa[x]=Find(fa[x]);}
inline void Link(ll x,ll y){ll xx=Find(x),yy=Find(y);if(xx==yy) return;fa[xx]=yy;}
inline void Merge(ll u,ll v)
{
if(Find(u)==Find(v)) return;
merge(rot[Find(v)],rot[Find(u)],1,n);Link(u,v);
}
signed main(){
read(n),read(m);
fo(1,i,n) read(p[i]),upd(rot[i],1,n,p[i],i),fa[i]=i;
fo(1,i,m){ll u,v;read(u),read(v);Merge(u,v);}
read(q);
fo(1,i,q)
{
char c;cin>>c;
ll x,y;id++;read(x),read(y);
if(c=='Q') wr(ask(rot[Find(x)],1,n,y)),pr;
if(c=='B') Merge(x,y);
}
return 0;
}
例题2:P4556 [Vani有约会] 雨天的尾巴 /【模板】线段树合并
一棵树,
首先可以对路径添加做差分转化,对
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3f
#define inf 0x3f3f3f3f
#define pr putchar('\n')
#define fi first
#define se second
#define pp putchar(' ')
#define pii pair<ll,ll>
#define pdi pair<ll,ll>
#define mem(aa,bb) memset(aa,bb,sizeof(aa))
#define fo(a,i,b) for(register ll i = a ; i <= b ; ++ i )
#define Fo(a,i,b) for(register ll i = a ; i >= b ; -- i )
#define pb push_back
//#pragma GCC optimize(2)
using namespace std;
typedef int ll;
// typedef long long ll;
//typedef __int128 ll;
typedef double db;
inline void read(ll &opp){ll x=0,t=1;char ch;ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-'){t=-1;}ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}opp=x*t;return; }
inline void wr(ll x){if(x<0){putchar('-');x=-x;}if(x>9){wr(x/10);}putchar(x%10+'0');}
const ll N=2e6+5,M=2e4+5,mod=1e9+7;
ll n,m,siz[N],fa[N],dep[N],top[N],w[N],id[N],son[N],cnt,opt,ans[N],rot[N];
vector<ll> g[N];
struct SGT{ll l,r,sum,id;}tree[N<<2];
#define rt tree[root]
#define ls tree[tree[root].l]
#define rs tree[tree[root].r]
inline void pushup(ll root){if(ls.sum>rs.sum) rt.sum=ls.sum,rt.id=ls.id;else if(ls.sum<rs.sum) rt.sum=rs.sum,rt.id=rs.id;else rt.sum=ls.sum,rt.id=min(ls.id,rs.id);}
inline void upd(ll &root,ll l,ll r,ll x,ll k){
if(!root) root=++opt;
if(l==r){rt.sum+=k;rt.id=l;return;}
ll mid=l+r>>1;if(x<=mid) upd(rt.l,l,mid,x,k);else upd(rt.r,mid+1,r,x,k);
pushup(root);
}
inline void merge(ll &rt1,ll rt2,ll l,ll r)
{
if(!rt1) return rt1=rt2,void();if(!rt2) return;
if(l==r){tree[rt1].sum+=tree[rt2].sum;tree[rt1].id=l;return;}
ll mid=l+r>>1;
merge(tree[rt1].l,tree[rt2].l,l,mid);
merge(tree[rt1].r,tree[rt2].r,mid+1,r);
pushup(rt1);
}
inline void dfs1(ll x,ll fat,ll depth){dep[x]=depth,fa[x]=fat,siz[x]=1;for(ll y:g[x]){if(y==fa[x]) continue;dfs1(y,x,depth+1);siz[x]+=siz[y];if(siz[y]>siz[son[x]]) son[x]=y;}}
inline void dfs2(ll x,ll nowtop){top[x]=nowtop,id[x]=++cnt,w[cnt]=x;if(!son[x]) return;dfs2(son[x],nowtop);for(ll y:g[x]){if(y==fa[x]||y==son[x]) continue;dfs2(y,y);}}
inline ll LCA(ll x,ll y){while(top[x]^top[y]) dep[top[x]]<dep[top[y]]?y=fa[top[y]]:x=fa[top[x]];return dep[x]<dep[y]?x:y;}
inline void dfs(ll x,ll fa){
for(ll y:g[x])
{
if(y==fa) continue;
dfs(y,x),merge(rot[x],rot[y],1,1e5);
}
// wr(x),pp,wr(tree[rot[x]].sum),pr;
ans[x]=tree[rot[x]].id;
}
signed main(){
read(n),read(m);fo(1,i,n-1){ll u,v;read(u),read(v);g[u].pb(v),g[v].pb(u);}
dfs1(1,0,1),dfs2(1,1);
fo(1,i,m){ll x,y,z;read(x),read(y),read(z);upd(rot[x],1,1e5,z,1),upd(rot[y],1,1e5,z,1),upd(rot[fa[LCA(x,y)]],1,1e5,z,-1),upd(rot[LCA(x,y)],1,1e5,z,-1);}
dfs(1,0);fo(1,i,n) wr(ans[i]),pr;
return 0;
}
P3605 [USACO17JAN] Promotion Counting P
模版,线段树合并的
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3f
#define inf 0x3f3f3f3f
#define pr putchar('\n')
#define fi first
#define se second
#define pp putchar(' ')
#define pii pair<ll,ll>
#define pdi pair<ll,ll>
#define mem(aa,bb) memset(aa,bb,sizeof(aa))
#define fo(a,i,b) for(register ll i = a ; i <= b ; ++ i )
#define Fo(a,i,b) for(register ll i = a ; i >= b ; -- i )
#define pb push_back
//#pragma GCC optimize(2)
using namespace std;
typedef int ll;
// typedef long long ll;
//typedef __int128 ll;
typedef double db;
inline void read(ll &opp){ll x=0,t=1;char ch;ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-'){t=-1;}ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}opp=x*t;return; }
inline void wr(ll x){if(x<0){putchar('-');x=-x;}if(x>9){wr(x/10);}putchar(x%10+'0');}
const ll N=5e6+5,M=2e4+5,mod=1e9+7;
ll n,p[N],ans[N],cnt,rot[N],x;
vector<ll> g[N];
struct SGT{ll l,r,sum;}tree[N<<2];
#define rt tree[root]
#define ls tree[tree[root].l]
#define rs tree[tree[root].r]
inline void pushup(ll root){rt.sum=ls.sum+rs.sum;}
inline void upd(ll &root,ll l,ll r,ll x,ll k){if(!root) root=++cnt;if(l==r) return rt.sum+=k,void();ll mid=l+r>>1;if(x<=mid) upd(rt.l,l,mid,x,k);else upd(rt.r,mid+1,r,x,k);pushup(root);}
inline void merge(ll &rt1,ll rt2,ll l,ll r){if(!rt1) return rt1=rt2,void();if(!rt2) return;if(l==r) return tree[rt1].sum+=tree[rt2].sum,void();ll mid=l+r>>1;merge(tree[rt1].l,tree[rt2].l,l,mid),merge(tree[rt1].r,tree[rt2].r,mid+1,r);pushup(rt1);}
inline ll ask(ll root,ll l,ll r,ll x,ll y){if(!root) return 0;if(x<=l&&y>=r) return rt.sum;ll mid=l+r>>1,ans=0;if(x<=mid) ans+=ask(rt.l,l,mid,x,y);if(y>mid) ans+=ask(rt.r,mid+1,r,x,y);return ans;}
inline void dfs(ll x,ll fa){for(ll y:g[x]) if(y!=fa) dfs(y,x),merge(rot[x],rot[y],1,1e9);ans[x]=ask(rot[x],1,1e9,p[x]+1,1e9);}
signed main(){
read(n);fo(1,i,n) read(p[i]),upd(rot[i],1,1e9,p[i],1);
fo(2,i,n) read(x),g[x].pb(i),g[i].pb(x);
dfs(1,0);fo(1,i,n) wr(ans[i]),pr;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】