【JZOJ 3397】 雨天的尾巴 线段树合并

Description

一棵树,有若干操作,每个操作包含三个数x,y,z,表示在树上从x到y的路径上的所有点的z颜色的权值+1。所有操作结束后询问每个点权值最大的颜色是什么。

对于100% 的数据,1 <= n,m <= 100000; 1 <= x, y <= n; 1 <= z <= 10^9

Analysis

这道题是用的线段覆盖的思想。只不过放在树上。
我们想树上的两个点u,v之间的路径可以由(root,u)+(root,v)-(root,lca(u,v))-(root,fa(lca(u,v)))构成。
所以线段覆盖放在树上可以转化成每个点信息的合并。即从下往上做,每个点的信息与其所有子节点合并。
在这里,显然每个点的信息就是颜色权值了。
怎么记录?权值线段树!空间会爆?首先把操作的z离散化,然后动态开节点!
然后就是线段树的操作了。
由于本人弱,这是第一道线段树合并的题。
两棵线段树合并,对应的节点搜下来,若一个为空就返回另一个,否则合并节点的左右子节点。合并到叶子节点就将权值直接“+”一下(这个+指满足结合律的所有运算)
2017.5.8更
发现并不会证线段树合并复杂度
上网搜到了Po姐的证明,怒%一发

Po姐:
我定义一棵线段树的势为节点个数
那么合并两棵线段树的代价为 两棵线段树的势和-新线段树的势
然后一开始所有线段树的势之和为O(nlogn)
一个线段树上有logn个节点
合并到最后有nlogn个节点
所以最后的势能差是O(nlogn)
完事了

太强啦><
然后这题就能时间空间都是nlogn了

Key Code

我自己原始的code太恶心,于是观摩了一下alan_cty的,果然大神就是大神,代码简洁明了又实用%%%

int merge(int x,int y,int l,int r)
{
    if(!x || !y) return x+y;
    if(l==r)
    {
        a[x].mx+=a[y].mx;
        return x;
    }
    int mid=(l+r)>>1;
    a[x].l=merge(a[x].l,a[y].l,l,mid);
    a[x].r=merge(a[x].r,a[y].r,mid+1,r);
    a[x].mx=max(a[a[x].l].mx,a[a[x].r].mx);
    return x;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值