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;
}