洛谷P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并 题解
题目链接:P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并
题意:村落里的一共有 $n$ 座房屋,并形成一个树状结构。然后救济粮分 $m$ 次发放,每次选择两个房屋 $(x,y)$,然后对于 $x$ 到 $y$ 的路径上(含 $x$ 和 $y$)每座房子里发放一袋 $z$ 类型的救济粮。
然后深绘里想知道,当所有的救济粮发放完毕后,每座房子里存放的最多的是哪种救济粮
显然这是板子题
我们利用树上差分的思想,自底向上统计答案
同时一路合并每个节点的线段树,即可
本文主要讲一讲为什么数组要开 1e5*70
左右
首先 $z \le 10^5$ ,取最大值 $Z=10^5$ (权值线段树嘛)
其次,动态开点线段树一次从根节点到叶子节点的修改操作
即 modify()
新建的点数为 $O(\log Z)$ 的,在本题中 $\log Z \approx \log_2 10^5 \approx 17$
而观察代码可以发现,我们做树上差分的时候,一次差分会修改至多 $4$ 个节点
因此开的空间大小就是 $Z\log Z\times 4$ ,稍微取个整就是 1e5*70
了
贴个代码,方便各位食用
#include <bits/stdc++.h>
using namespace std;
// #define int long long
#define INF 0x3f3f3f3f3f3f3f3f
namespace FastIO
{
#define gc() readchar()
#define pc(a) putchar(a)
#define SIZ (int)(1e6+15)
char buf1[SIZ],*p1,*p2;
char readchar()
{
if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);
return p1==p2?EOF:*p1++;
}
template<typename T>void read(T &k)
{
char ch=gc();T x=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
k=x*f;
}
template<typename T>void write(T k)
{
if(k<0){k=-k;pc('-');}
static T stk[66];T top=0;
do{stk[top++]=k%10,k/=10;}while(k);
while(top){pc(stk[--top]+'0');}
}
}using namespace FastIO;
#define N (int)(1e5+15)
int n,m,ans[N];
struct Edge
{
int u,v,next;
}e[N<<1];
int pos=1,head[N];
int fa[N],son[N],sz[N],top[N],dep[N];
int val[N*70],ch[N*70][2],rt[N],tot,Z,typ[N*70];
#define ls(at) ch[at][0]
#define rs(at) ch[at][1]
void addEdge(int u,int v)
{
e[++pos]={u,v,head[u]};
head[u]=pos;
}
void dfs(int u,int f,int d)
{
fa[u]=f;
sz[u]=1;
dep[u]=d;
int mx=-1;
for(int i=head[u]; i; i=e[i].next)
{
int v=e[i].v;
if(v==f)continue;
dfs(v,u,d+1);
sz[u]+=sz[v];
if(sz[v]>mx)mx=sz[v],son[u]=v;
}
}
void dfs(int u,int ftop)
{
top[u]=ftop;
if(!son[u])return;
dfs(son[u],ftop);
for(int i=head[u]; i; i=e[i].next)
{
int v=e[i].v;
if(v==fa[u]||v==son[u])continue;
dfs(v,v);
}
}
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]];
}
return dep[x]<dep[y]?x:y;
}
void push_up(int at)
{
if(val[ls(at)]>=val[rs(at)])
val[at]=val[ls(at)],typ[at]=typ[ls(at)];
else val[at]=val[rs(at)],typ[at]=typ[rs(at)];
}
void modify(int l,int r,int pos,int v,int &at)
{
if(!at)at=++tot;
if(l==r)
{
val[at]+=v;
typ[at]=l;
return;
}
int mid=(l+r)>>1;
if(pos<=mid)modify(l,mid,pos,v,ls(at));
else modify(mid+1,r,pos,v,rs(at));
push_up(at);
}
int merge(int x,int y,int l,int r)
{
if(!x||!y)return x|y;
if(l==r)
{
val[x]+=val[y];
typ[x]=l;
return x;
}
int mid=(l+r)>>1;
ls(x)=merge(ls(x),ls(y),l,mid);
rs(x)=merge(rs(x),rs(y),mid+1,r);
push_up(x);
return x;
}
void calc(int u)
{
for(int i=head[u]; i; i=e[i].next)
{
int v=e[i].v;
if(v==fa[u])continue;
calc(v);
rt[u]=merge(rt[u],rt[v],1,Z);
}
if(val[rt[u]])ans[u]=typ[rt[u]];
}
int u[N],v[N],z[N];
signed main()
{
// ios::sync_with_stdio(0);
// cin.tie(0);cout.tie(0);
read(n);read(m);
for(int i=1,u,v; i<n; i++)
{
read(u);read(v);
addEdge(u,v);addEdge(v,u);
}
dfs(1,0,1);dfs(1,1);
for(int i=1; i<=m; i++)
read(u[i]),read(v[i]),read(z[i]),Z=max(Z,z[i]);
for(int i=1; i<=m; i++)
{
int d=lca(u[i],v[i]);
modify(1,Z,z[i],1,rt[u[i]]);
modify(1,Z,z[i],1,rt[v[i]]);
modify(1,Z,z[i],-1,rt[d]);
modify(1,Z,z[i],-1,rt[fa[d]]);
}
calc(1);
for(int i=1; i<=n; i++)
write(ans[i]),pc('\n');
return 0;
}