Description
深绘里一直很讨厌雨天。
灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切。
虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连
根拔起,以及田地里的粮食被弄得一片狼藉。
无奈的深绘里和村民们只好等待救济粮来维生。
不过救济粮的发放方式很特别。
首先村落里的一共有n 座房屋,并形成一个树状结构。然后救济粮分m 次发放,每次选择
两个房屋(x,y),然后对于x 到y 的路径上(含x 和y) 每座房子里发放一袋z 类型的救济粮。
然后深绘里想知道,当所有的救济粮发放完毕后,每座房子里存放的最多的是哪种救济粮
Solution
一道树上路径修改问题
比较恶心的是,最多有 100000 种救济粮
我们先考虑在一个序列上区间修改
把每个区间左边界排序,左边界+1,右边界-1,从左到右扫一遍扔进堆里,然后堆顶就是当前答案
放到树上可以树链剖分,对于每个链操作都是独立的,分开处理就好。
但是,你有没有觉得很麻烦?
仔细研究题目,发现询问并不是动态的。
对于树上每一个点,都维护一个动态开点的以种类为下标的权值线段树,记录最大值和位置。
显然我们不能每个操作都去路径维护。
对于下面这个图
对于多次路径修改,有一种十分经典的做法——线段树合并
关于线段树合并不理解的可以看这里
我们需要对
x,y
进行路径修改,那么可以将
x,y
都加上1
在
lca(x,y),fa[lca(x,y)]
都减1
想想为什么 fa[lca(x,y)] 也要减1?
这样到了最后线段树合并的时候,从叶子节点向根逐渐合并,到了 lca(x,y) 的时候就会有 x,y 的影响之和,所以要减去1,在 fa[lca(x,y)] 减1是为了让 lca(x,y) 以上不受影响
这样可以 logMaxz 修改, NlogMaxz 合并的
有人可能会有疑问,不是每个点都要开一个权值线段树 NlogMaxz 空间?合并时候更是 NMlogMaxz ?
不虚,我们有动态开节点!
因为每次操作最多新开
4logMaxz
个节点,最后最多总共也只有
4MlogMaxz
个,
合并时并不需要多开节点,详见上面链接
于是我们在 O(NlogMaxz) 复杂度内解决,然而链剖是需要 O(Nlog2N) 的
Code
本人特别讨厌线段树,因为每次我都把线段树打的很长很长~~
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
#define fo1(i,a) for(int i=last[k];i;i=next[i])
using namespace std;
struct note
{
int l,r,mx,wz,ls,rs;
};
struct note1
{
int x,y,z;
};
bool cmp(note1 x,note1 y)
{
return x.z<y.z;
}
note tree[5000000];
int next[200005],dt[200005],last[100005],first[100005];
int n,m,a1[100005][2],ft[100005][17],root[100005],deep[100005],lim,dp,rlim,maxn;
int pt[100001];
note1 cz[100001];
void dfs(int k,int f)
{
ft[k][0]=f;
deep[k]=deep[f]+1;
dp=deep[k];
if (deep[k]==43423)
{
dp++;
dp--;
}
fo1(b,k)
if (dt[b]!=f) dfs(dt[b],k);
}
int lca(int x,int y)
{
if (deep[x]<deep[y]) swap(x,y);
while(deep[x]!=deep[y])
{
int i=0;
while(deep[ft[x][i]]>=deep[y]) i++;
x=ft[x][--i];
}
while (x!=y)
{
int i=0;
while (ft[x][i]!=ft[y][i]) i++;
if (i==0) return ft[x][0];
x=ft[x][--i];
y=ft[y][i];
}
return x;
}
void buildl(int now,int l,int r)
{
if (tree[now].ls==0)
{
tree[now].ls=++lim;
tree[lim].l=l;
tree[lim].r=r;
}
}
void buildr(int now,int l,int r)
{
if (tree[now].rs==0)
{
tree[now].rs=++lim;
tree[lim].l=l;
tree[lim].r=r;
}
}
void change(int now,int l,int r,int v)
{
if (tree[now].l==l&&tree[now].r==r)
{
tree[now].mx+=v;
tree[now].wz=l;
return;
}
int mid=(tree[now].l+tree[now].r)/2;
if(r<=mid)
{
buildl(now,tree[now].l,mid);
change(tree[now].ls,l,r,v);
}
else if (l>mid)
{
buildr(now,mid+1,tree[now].r);
change(tree[now].rs,l,r,v);
}
else
{
buildl(now,tree[now].l,mid);
buildr(now,mid+1,tree[now].r);
change(tree[now].ls,l,mid,v);
change(tree[now].rs,mid+1,r,v);
}
int ls=tree[now].ls,rs=tree[now].rs;
int x=tree[ls].mx,y=tree[rs].mx,x1=tree[ls].wz,y1=tree[rs].wz;
tree[now].mx=(x>=y)?x:y;
tree[now].wz=(x>=y)?x1:y1;
}
void hb(int hf,int hs,int l,int r)
{
if(l==r)
{
tree[hf].mx+=tree[hs].mx;
tree[hf].wz=l;
return;
}
int lsf=tree[hf].ls,rsf=tree[hf].rs,lss=tree[hs].ls,rss=tree[hs].rs,mid=(l+r)/2;
if (lss!=0)
{
if (lsf==0) tree[hf].ls=lss;
else hb(lsf,lss,l,mid);
}
if (rss!=0)
{
if (rsf==0) tree[hf].rs=rss;
else hb(rsf,rss,mid+1,r);
}
int x=tree[tree[hf].ls].mx,y=tree[tree[hf].rs].mx,x1=tree[tree[hf].ls].wz,y1=tree[tree[hf].rs].wz;
tree[hf].mx=(x>=y)?x:y;
tree[hf].wz=(x>=y)?x1:y1;
}
void merge(int k,int f)
{
int i;
for(int i=last[k];i;i=next[i])
{
if (dt[i]!=f)
{
merge(dt[i],k);
hb(root[k],root[dt[i]],1,maxn);
}
}
}
int main()
{
cin>>n>>m;
int i,j;
rlim=0;
fo(i,1,n-1)
{
int x,y;
rlim++;
scanf("%d%d",&x,&y);
dt[rlim]=x;
if (last[y]==0) last[y]=first[y]=rlim;
else
{
next[first[y]]=rlim;
first[y]=rlim;
}
rlim++;
dt[rlim]=y;
if (last[x]==0) last[x]=first[x]=rlim;
else
{
next[first[x]]=rlim;
first[x]=rlim;
}
}
dfs(1,0);
fo(i,1,trunc(log(n)/log(2)))
fo(j,1,n) ft[j][i]=ft[ft[j][i-1]][i-1];
fo(i,1,m)
{
int x,y,z;
scanf("%d%d%d",&cz[i].x,&cz[i].y,&cz[i].z);
maxn=100000;
}
sort(cz+1,cz+m+1,cmp);
int bi=0;
fo(i,1,n)
{
root[i]=++lim;
tree[lim].l=1;
tree[lim].r=100000;
}
fo(i,1,m)
{
if (cz[i].z!=cz[i-1].z) bi++;
pt[bi]=cz[i].z;
int x=cz[i].x,y=cz[i].y;
int lp=lca(x,y);
change(root[x],bi,bi,1);
change(root[y],bi,bi,1);
change(root[lp],bi,bi,-1);
if (lp!=1) change(root[ft[lp][0]],bi,bi,-1);
}
merge(1,0);
fo(i,1,n) printf("%d\n",pt[tree[root[i]].wz]);
}