[學習筆記]線段樹合併
阿新 • • 發佈:2018-11-15
1、[POI2011]ROT-Tree Rotations
分析:線段樹合併人生第一題。
網上的題解我都沒看懂……我自己講一下好了
線段樹合併就是把兩棵權值線段樹合併到一棵
那怎麼合併呢?
假設有這麼兩棵樹:
一個結點代表一段值域區間有幾個數,那麼可以看出合併後應該是這樣的
然後具體步驟就是找到一個結點,如果一個結點一棵樹上有一棵樹上沒有,那麼直
接返回那個結點的編號,否則兩個值域的和相加,遞迴至左兒子和右兒子
每次合併的複雜度為兩棵線段樹的點數相加
這道題可以算出左兒子的逆序對個數,右兒子的逆序對個數,然後求出 \((x,y)\) 的對數, $x\in $ 左兒子, $y\in $ 右兒子
每次記錄一下 \(sum\),在每次合併的時候 \(sum[lson[x]]\times sum[rson[y]]\) 和 \(sum[rson[x]]\times 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\n",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\n",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("\n");
return 0;
}
搞了一個下午線段樹合併,還是很有成就感滴!