前言
一般来说,线段树会有
O
(
n
)
O(n)
O(n) 个节点。但是有的时候,整棵线段树就只进行了一次插入操作,这样只会有
O
(
l
o
g
n
)
O(logn)
O(logn) 个节点。
处理树上问题时,我们有时需要把儿子的信息合并到父亲节点。这个时候可以使用 dsu on tree,常数小,但复杂度多一个
l
o
g
log
log。或者,我们也可以使用线段树合并。
算法过程
必须使用动态开点线段树。
我们需要把线段树
v
v
v 合并到线段树
u
u
u
- 传入两棵线段树的根,以及它们代表的区间范围。
- 如果此时区间长度为1,那么就直接合并两个点。
- 如果两棵线段树都有左儿子,那么就尝试合并左儿子
- 如果只有 v v v 有左儿子,那么令 u u u 的左儿子等于 v v v 的左儿子
- 右儿子类似
核心代码
void merge(int u,int v,int st,int ed)
{
if(st==ed)
{
tr[u].num+=tr[v].num;
tr[u].sum+=tr[v].sum;
tr[u].val+=tr[v].val;
return;
}
int mid=st+ed>>1;
if(tr[u].ls&&tr[v].ls)
{
merge(tr[u].ls,tr[v].ls,st,mid);
}
else if(tr[v].ls)
{
tr[u].ls=tr[v].ls;
}
if(tr[u].rs&&tr[u].rs)
{
merge(tr[u].rs,tr[v].rs,mid+1,ed);
}
else
{
tr[u].rs=tr[v].rs;
}
update(u);
}
洛谷P4556 [Vani有约会] 雨天的尾巴 /【模板】线段树合并
思路
树上差分+线段树合并
最朴素的想法是每个点开一个vector,对于每一次发放救济粮就对那一种救济粮进行树上差分。然后最后dfs一遍,每次暴力合并儿子的vector。
但显然这样就会TLE,时间复杂度
O
(
n
2
)
O(n^2)
O(n2)
我们不妨用权值线段树代替vector,合并儿子信息时使用线段树合并,这样就可以做到
O
(
n
log
n
)
O(n\log n)
O(nlogn)
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+7,inf=1e9,S=20;
vector<vector<int>> e,fa,a;
vector<int> dep;
int n,m;
struct seg
{
int ls,rs,mx,id;
seg()
{
ls=rs=id=0;
mx=0;
}
seg(int a,int b,int c,int d):ls(a),rs(b),mx(c),id(d)
{
}
friend bool operator < (const seg &a,const seg &b)
{
return a.mx==b.mx?a.id>b.id:a.mx<b.mx;
}
friend seg operator + (const seg &a,const seg &b)
{
return seg(a.ls,a.rs,a.mx+b.mx,a.id);
}
};
vector<seg> tr;
void update(int u)
{
int ls=tr[u].ls,rs=tr[u].rs;
if(tr[ls]<tr[rs])
{
tr[u].mx=tr[rs].mx;
tr[u].id=tr[rs].id;
}
else
{
tr[u].mx=tr[ls].mx;
tr[u].id=tr[ls].id;
}
}
void merge(int u,int v,int st,int ed)
{
if(st==ed)
{
tr[u]=tr[u]+tr[v];
return;
}
int mid=st+ed>>1;
if(tr[u].ls&&tr[v].ls)
merge(tr[u].ls,tr[v].ls,st,mid);
else if(tr[v].ls)
tr[u].ls=tr[v].ls;
if(tr[u].rs&&tr[v].rs)
merge(tr[u].rs,tr[v].rs,mid+1,ed);
else if(tr[v].rs)
tr[u].rs=tr[v].rs;
update(u);
}
void insert(int u,int st,int ed,int x,int t)
{
if(st==ed&&ed==x)
{
tr[u].mx+=t;
tr[u].id=x;
return;
}
int mid=st+ed>>1;
if(x<=mid)
{
if(!tr[u].ls)
{
tr.push_back(seg());
tr[u].ls=tr.size()-1;
// cerr<<tr.size()-1<<"\n";
}
insert(tr[u].ls,st,mid,x,t);
}
else
{
if(!tr[u].rs)
{
tr.push_back(seg());
tr[u].rs=tr.size()-1;
// cerr<<tr.size()-1<<"\n";
}
insert(tr[u].rs,mid+1,ed,x,t);
}
update(u);
}
void dfs1(int u)
{
dep[u]=dep[fa[u][0]]+1;
for(auto v:e[u])
{
if(v==fa[u][0]) continue;
fa[v][0]=u;
dfs1(v);
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y])
swap(x,y);
for(int i=S-1; i>=0; i--)
{
if(dep[fa[x][i]]>=dep[y])
x=fa[x][i];
}
if(x==y) return x;
for(int i=S-1; i>=0; i--)
{
if(fa[x][i]!=fa[y][i])
{
x=fa[x][i];
y=fa[y][i];
}
}
return fa[x][0];
}
vector<int> ans;
void dfs2(int u)
{
for(auto v:e[u])
{
if(v==fa[u][0]) continue;
dfs2(v);
merge(u,v,1,N);
}
for(auto x:a[u])
{
if(x>0)
insert(u,1,N,x,1);
else
insert(u,1,N,-x,-1);
}
ans[u]=tr[u].mx>0?tr[u].id:0;
}
void O_o()
{
cin>>n>>m;
e.assign(n+1,vector<int>());
for(int i=1; i<n; i++)
{
int x,y;
cin>>x>>y;
e[x].push_back(y);
e[y].push_back(x);
}
fa.assign(n+1,vector<int>(S));
dep.assign(n+1,0);
dfs1(1);
for(int j=1; j<S; j++)
{
for(int i=1; i<=n; i++)
{
fa[i][j]=fa[fa[i][j-1]][j-1];
}
}
a.assign(n+1,vector<int>());
tr.assign(n+1,seg());
for(int i=1; i<=m; i++)
{
int x,y,z;
cin>>x>>y>>z;
int l=lca(x,y);
a[x].push_back(z);
a[y].push_back(z);
a[l].push_back(-z);
a[fa[l][0]].push_back(-z);
}
ans.assign(n+1,0);
dfs2(1);
for(int i=1; i<=n; i++)
{
cout<<ans[i]<<"\n";
}
}
signed main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cout<<fixed<<setprecision(12);
int T=1;
// cin>>T;
while(T--)
{
O_o();
}
}