世界真的很大
前几次考试有一次树链剖分当场写挂之后调了一下午
一直耿耿于怀,于是乎找一道树链剖分的题来练手
虽然代码量略大但是调试起来还是比较轻松,一个小错误卡了一会儿
没搞明白root根本没有赋值为什么还能过样例
一直RE加上return就A了
看题先:
description:
给定一棵有n个节点的无根树和m个操作,操作有2类:
1、将节点a到节点b路径上所有点都染成颜色c;
2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“ 112221 ” 由3段组成:“ 11 ” 、“ 222 ” 和“ 1 ” 。
请你写一个程序依次完成这m个操作。
input:
第一行包含2个整数n和m,分别表示节点数和操作数;
第二行包含n个正整数表示n个节点的初始颜色
下面 行每行包含两个整数x和y,表示x和y之间有一条无向边。
下面 行每行描述一个操作:
“C a b c”表示这是一个染色操作,把节点a到节点b路径上所有点(包括a和b)都染成颜色c;
“Q a b”表示这是一个询问操作,询问节点a到节点b(包括a和b)路径上的颜色段数量。
output:
对于每个询问操作,输出一行答案。
树上链修改和查询,1e5的数据范围,应该能想到树链剖分
树链剖分的目的主要是为了那个特殊的DFS序,不光满足子树的DFS序是连续的一段,还满足一条重链上的点的dfs序是连续的一段
重链就是指子树最大的子节点所组成的链
这样就把“树”这么一个图抽象成了序列,然后就可以用数据结构维护序列的信息,然后通过序列与树的关系来得到答案
树上的链可以由一条条重链的一部分拆分得到,而重链由在DFS序上是连续的一段,所以可以用线段树来维护。
由于一条链最多被拆分成log条重链,而一次线段树查询时log的,那么一次链询问就是log^2的,总复杂度就是mlog^2的
这道题我们求的是链上有多少颜色段,那么线段树的一个区间就保存颜色段的数量,由于树上链会被拆分,于是乎会多次查询线段,有可能两条线段拼起来会形成一段颜色,这样就会重复计数。所以想到再记录一下每个线段两个端点的颜色,如果上一条线段的做断电与这一条线段的右端点相等的话,ans就–
还有染色操作,就是区间覆盖,搞一个标记flag,在查询时注意pushdown就行了,还是比较好写
完整代码:
#include<stdio.h>
#include<algorithm>
using namespace std;
struct edge
{
int v,last;
}ed[400010];
struct node
{
int sum,lc,rc,flag;
node *ls,*rs;
void pushdown()
{
if(flag)
{
ls->sum=rs->sum=