線段樹合並說全來就是動態開點權值線段樹合並。所以你需要掌握權值線段樹的基本知識以及知道什么是動態開點(霧
線段樹合並的主要方式如下:
對於兩棵線段樹都有的節點,新的線段樹的該節點值為兩者和。
對於某一棵線段樹有的節點,新的線段樹保存該節點的值。
然后對左右子樹遞歸處理。
不能理解?那就看一下代碼。
int merge (int l, int r, int u, int v)
{
if (!u || !v) return u|v;
if (l==r) {return val[++tot]=val[u]+val[v], tot;}
int mid=l+r>>1, node=++tot;
//若干操作
ls[node]=merge(l, mid, ls[u], ls[v]);
rs[node]=merge(mid+1, r, rs[u], rs[v]);
val[node]=val[ls[node]]+val[rs[node]];
return node;
}
看起來很暴力?那么復雜度是多少?
合並的復雜度顯然與兩棵線段樹重合的葉子結點個數有關,實際上若葉子個數為\(m\),那么一次合並的復雜度就是\(mlogn\)了。
我要合並\(n\)次怎么辦?復雜度\(O(nmlogn)\)?
其實是\(O(nlogn)\),可是我不會證。引用洛谷日報的證明:
來證明一下:
假設我們會加入\(k\)個點,由上面的結論,我們可以推出最多要新增\(klogk\)個點。
而正如我們所知,每次合並兩棵線段樹同位置的點,就會少掉一個點,復雜度為\(O(1)\) ,總共\(O(klogk)\)個點,全部合並的復雜度就是\(O(klogk)\)
給出一棵\(n\)個葉子的二叉樹,僅葉子有點權,求通過變換任意幾個點的左右子樹使得前序遍歷葉子的逆序對最小值。
\(dfs\)整棵樹,對於一個節點,交換子樹使逆序對更少則交換。如何求逆序對?線段樹合並即可。
不過該題需要注意寫法,不能每次合並到新開的節點,而應直接將線段樹\(a\)合並到線段樹\(b\)上,不然會\(MLE\)。
#include<cstdio>
#define rep(i, a, b) for (register int i=(a); i<=(b); ++i)
#define per(i, a, b) for (register int i=(a); i>=(b); --i)
using namespace std;
const int N=6000005;
long long min(long long a, long long b){return a<b?a:b;}
int ls[N], rs[N], val[N], n, tot;
long long ans, ans1, ans2;
inline int read()
{
int x=0,f=1;char ch=getchar();
for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return x*f;
}
int New(int l, int r, int x)
{
val[++tot]=1;
if (l==r) return tot;
int mid=l+r>>1, node=tot;
if (x<=mid) ls[node]=New(l, mid, x);
else rs[node]=New(mid+1, r, x);
return node;
}
int merge(int l, int r, int u, int v)
{
if (!u || !v) return u+v;
if (l==r) {val[u]=val[u]+val[v]; return u;}
int mid=(l+r)>>1, node=u;
ans1+=1ll*val[rs[u]]*val[ls[v]];
ans2+=1ll*val[ls[u]]*val[rs[v]];
ls[node]=merge(l, mid, ls[u], ls[v]);
rs[node]=merge(mid+1, r, rs[u], rs[v]);
val[node]=val[ls[node]]+val[rs[node]];
return node;
}
int dfs()
{
int v=read();
if (v) return New(1, n, v);
int node=merge(1, n, dfs(), dfs());
ans+=min(ans1, ans2); ans1=ans2=0;
return node;
}
int main()
{
n=read(); dfs(); printf("%lld\n", ans);
return 0;
}
另一道題目:雨天的尾巴。
題意如下:給定一棵樹,每次對一條路徑上的每個節點的可重集中加入一個數。求若干次操作后每個節點可重集中出現最多的元素。
樹上路徑統計信息的題很容易想到樹上差分,如果是\(u->v\)的路徑,在\(u,v\)出\(+1\),在\(LCA(u,v),fa_{LCA(u,v)}\)處\(-1\)。因為要維護多種元素,所以每個節點開一個權值線段樹。
然后最后從下到上線段樹合並即可。
時間復雜度\(O(nlogn)\)
本題空間略卡,所以線段樹合並寫的是覆蓋式的而不是新開節點,並且寫了樹剖\(LCA\)
#include<cstdio>
#include<vector>
#define rep(i, a, b) for (register int i=(a); i<=(b); ++i)
#define per(i, a, b) for (register int i=(a); i>=(b); --i)
using namespace std;
const int N=100005, M=30000005;
int dep[N], size[N], fa[N], son[N], top[N];
int rt[M], ls[M], rs[M], Max[M], Tag[M];
int x[N], y[N], z[N], ans[N], tot, R, n, m;
vector<int> G[N];
inline int read()
{
int x=0,f=1;char ch=getchar();
for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return x*f;
}
void dfs1(int u, int f)
{
dep[u]=dep[f]+1; size[u]=1; fa[u]=f;
for (int v: G[u]) if (v^f)
{
dfs1(v, u); size[u]+=size[v];
if (size[v]>size[son[u]]) son[u]=v;
}
}
void dfs2(int u, int t)
{
top[u]=t;
if (son[u]) dfs2(son[u], t);
for (int v: G[u]) if (v^son[u] && v^fa[u]) dfs2(v, v);
}
int LCA(int u, int v)
{
while (top[u]^top[v])
{
if (dep[top[u]]<dep[top[v]]) swap(u, v);
u=fa[top[u]];
}
return dep[u]<dep[v]?u:v;
}
void pushup(int rt)
{
if (Max[ls[rt]]>=Max[rs[rt]])
Max[rt]=Max[ls[rt]], Tag[rt]=Tag[ls[rt]];
else Max[rt]=Max[rs[rt]], Tag[rt]=Tag[rs[rt]];
if (!Max[rt]) Tag[rt]=0;
}
void Modify(int &rt, int l, int r, int p, int v)
{
if (!rt) rt=++tot;
if (l==r) {Max[rt]+=v; Tag[rt]=l; return;}
int mid=l+r>>1;
if (p<=mid) Modify(ls[rt], l, mid, p, v);
else Modify(rs[rt], mid+1, r, p, v);
pushup(rt); return;
}
int merge(int u, int v, int l, int r)
{
if (!u || !v) return u|v;
if (l==r) {Max[u]+=Max[v]; Tag[u]=l; return u;}
int mid=l+r>>1;
ls[u]=merge(ls[u], ls[v], l, mid);
rs[u]=merge(rs[u], rs[v], mid+1, r);
pushup(u); return u;
}
void dfs(int u, int fa)
{
for (int v: G[u]) if (v^fa)
dfs(v, u), rt[u]=merge(rt[u], rt[v], 1, R);
ans[u]=Tag[rt[u]];
}
int main()
{
n=read(); m=read();
rep(i, 1, n-1)
{
int u=read(), v=read();
G[u].push_back(v); G[v].push_back(u);
}
dfs1(1, 0); dfs2(1, 1);
rep(i, 1, m) x[i]=read(), y[i]=read(), z[i]=read(), R=max(R, z[i]);
rep(i, 1, m)
{
int lca=LCA(x[i], y[i]);
Modify(rt[x[i]], 1, R, z[i], 1);
Modify(rt[y[i]], 1, R, z[i], 1);
Modify(rt[lca], 1, R, z[i], -1);
if (fa[lca]) Modify(rt[fa[lca]], 1, R, z[i], -1);
}
dfs(1, 0);
rep(i, 1, n) printf("%d\n", ans[i]);
return 0;
}