P4556 [Vani有约会] 雨天的尾巴 /【模板】线段树合并
P4556 [Vani有约会] 雨天的尾巴 /【模板】线段树合并
题目背景
深绘里一直很讨厌雨天。
灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切。
虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地里的粮食被弄得一片狼藉。
无奈的深绘里和村民们只好等待救济粮来维生。
不过救济粮的发放方式很特别。
题目描述
村落里一共有
然后深绘里想知道,当所有的救济粮发放完毕后,每座房子里存放的最多的是哪种救济粮。
输入格式
输入的第一行是两个用空格隔开的正整数,分别代表房屋的个数
第
第
输出格式
输出
如果某座房屋没有救济粮,则输出
说明/提示
- 对于
100\% 测试数据,保证1 \leq n, m \leq 10^5 ,1 \leq a,b,x,y \leq n ,1 \leq z \leq 10^5 。
Solution:
发现我线段树合并的板子题居然没写,顺手补一下。
首先我们看到题面不难想到树上差分一下,维护一个桶,表示当前的房屋下的救济粮的状态,我们需要维护两个值,
那么我们显然是希望这个桶可以向上合并的,不然我们就不好差分了,但是直接扫描一遍的话时间复杂度又会令人难以接受,所以我们需要一种十分快速的合并方法。不难发现,我们需要差分的点的个数是
那么我们如何实现快速的线段树合并呢?假设我们现在在合并两颗线段树
不难发现这样做的时间复杂度是
再有就是其实我们只差分了四个点,所以需要
感觉我的复杂度分析有点胡扯啊,如果以后想到更好的写法再补
Code:
#include <bits/stdc++.h>
using namespace std;
const int N=6e6+5;
int n,cnt,df,node,m,R;
int head[N],dep[N],fa[N],son[N],len[N],top[N],dfn[N],root[N];
int xx[N],yy[N],zz[N],num[N],ty[N],ls[N],rs[N],ans[N];
struct Edge{
int to,nxt;
}edge[N<<1];
void add(int x,int y)
{
edge[++cnt]=(Edge){y,head[x]};head[x]=cnt;
}
void init()
{
for(int i=1;i<=n+1;i++)
{
head[i]=-1;
}
}
void dfs1(int x,int f)
{
dep[x]=dep[f]+1;len[x]=1;fa[x]=f;
int maxlen=0;
for(int i=head[x];~i;i=edge[i].nxt)
{
int to=edge[i].to;
if(to==f)continue;
dfs1(to,x);
len[x]+=len[to];
if(len[to]>maxlen)
{
maxlen=len[to];
son[x]=to;
}
}
}
void dfs2(int x,int tp)
{
top[x]=tp;dfn[x]=++df;
if(!son[x])return;
dfs2(son[x],tp);
for(int i=head[x];~i;i=edge[i].nxt)
{
int to=edge[i].to;
if(to==fa[x]||to==son[x])continue;
dfs2(to,to);
}
}
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 y;
}
void pushup(int x)
{
if(num[ls[x]]>=num[rs[x]])
{
num[x]=num[ls[x]];
ty[x]=ty[ls[x]];
}
else
{
num[x]=num[rs[x]];
ty[x]=ty[rs[x]];
}
return ;
}
int upd(int x,int ll,int rr,int type,int val)
{
if(!x)x=++node;
if(ll==rr)
{
num[x]+=val;ty[x]=ll;
return x;
}
int mid=ll+rr>>1;
if(type<=mid)ls[x]=upd(ls[x],ll,mid,type,val);
else rs[x]=upd(rs[x],mid+1,rr,type,val);
pushup(x);
return x;
}
int merge(int x,int y,int ll,int rr)
{
if(!y)return x;
if(!x)return y;
if(ll==rr)
{
num[x]+=num[y];
ty[x]=ll;
return x;
}
int mid=ll+rr>>1;
ls[x]=merge(ls[x],ls[y],ll,mid);
rs[x]=merge(rs[x],rs[y],mid+1,rr);
pushup(x);
return x;
}
void ANS(int x)
{
for(int i=head[x];~i;i=edge[i].nxt)
{
int to=edge[i].to;
if(dep[to]<dep[x])continue;
ANS(to);root[x]=merge(root[x],root[to],1,R);
}
if(num[root[x]])
{
ans[x]=ty[root[x]];
}
}
int main()
{
//freopen("p4556.in","r",stdin);
//freopen("p4556.out","w",stdout);
cin>>n>>m;
init();
for(int i=1,x,y;i<n;i++)
{
cin>>x>>y;
add(x,y),add(y,x);
}
dfs1(1,0);
dfs2(1,1);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&xx[i],&yy[i],&zz[i]);
R=max(R,zz[i]);
}
for(int i=1;i<=m;i++)
{
int lca=Lca(xx[i],yy[i]);
root[xx[i]]=upd(root[xx[i]],1,R,zz[i],1);
root[yy[i]]=upd(root[yy[i]],1,R,zz[i],1);
root[lca]=upd(root[lca],1,R,zz[i],-1);
if(fa[lca])root[fa[lca]]=upd(root[fa[lca]],1,R,zz[i],-1);
}
ANS(1);
for(int i=1;i<=n;i++)
{
printf("%d\n",ans[i]);
}
return 0;
}