题意:

一颗 nnn 个节点的树,两个操作:
①①① 将 a−>ba->ba−>b 路径上的点都染成颜色 ccc
②②② 查询 a−>ba->ba−>b 路径上的颜色段数量


思路:

很明显是一个树剖问题,树剖的基础实现就不多说了,我们来考虑一下线段树需要维护什么。

首先求的是路径上不同颜色段数量,因此肯定需要维护一个 cntcntcnt,表示路径上不同颜色段的数量。然后我们来看如何实现区间合并,主要观察的就是左区间的右端点和右区间的左端点是否一样,如果一样,合并的 cntcntcnt 需要减 111。但是如何去快速查询左右节点颜色呢,因此可以想到再维护区间左右节点颜色即可完成本题。

树剖成多个树链时需要注意链条交界处的颜色是否一致。


总结:

可以发现树剖只是一种手段,将树分成多个链条只是有助于建立线段树,但真正考察的还是建线段树的能力。


代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define LOG1(x1,x2) cout << x1 << ": " << x2 << endl;
#define LOG2(x1,x2,y1,y2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << endl;
typedef long long ll;
typedef double db;
#define int long long
const db EPS = 1e-9;
const int N = 1e5+100;
using namespace std;struct Edge { int next,to;} e[2*N];
struct Node { int l,r,ls,rs,cnt,lazy,lc,rc;} t[2*N]; //cnt: 不同颜色个数, lc: 左端颜色, rc: 右端颜色
int n,m,root,rt,mod,val[N],head[N],tot,fa[N],d[N],son[N],size[N],top[N],id[N],rk[N];
//top[x]: x节点所在链的顶端节点, id[x]: 节点dfs序, rk[x]: dfs序对应的节点
//val[x]: 每个点初始权值, fa[x]: 每个点父节点, d[x]: 节点深度, size[x]: 节点子树大小
//rt: 线段树根节点编号
void init(){memset(head,0,sizeof head);tot = 1, size[0] = 0;
}void add(int x, int y){e[++tot].next = head[x], e[tot].to = y, head[x] = tot;
}void dfs1(int x){  //求出每个点的子树大小、深度、重儿子size[x] = 1, d[x] = d[fa[x]]+1, son[x] = 0;for(int v,i = head[x]; i; i = e[i].next)if((v = e[i].to)!=fa[x]){fa[v] = x, dfs1(v), size[x] += size[v];if(size[son[x]] < size[v])son[x] = v;}
}void dfs2(int x, int tp){  //求出每个节点的dfs序, dfs序对应的节点, 以及每个点所在链的顶端节点top[x] = tp, id[x] = ++tot, rk[tot] = x;if(son[x]) dfs2(son[x],tp);for(int v,i = head[x]; i; i = e[i].next)if((v = e[i].to)!=fa[x] && v!=son[x]) dfs2(v,v);
}inline void pushup(int x){ //基础的线段树向上区间合并t[x].lc = t[t[x].ls].lc, t[x].rc = t[t[x].rs].rc, t[x].cnt = t[t[x].ls].cnt + t[t[x].rs].cnt;if(t[t[x].ls].rc == t[t[x].rs].lc) t[x].cnt--;
}void build(int l, int r, int x){ //基础建树,动态开点t[x].l = l, t[x].r = r, t[x].lazy = -1;if(l == r){t[x].lc = t[x].rc = val[rk[l]], t[x].cnt = 1; return;}int mid = (l+r)>>1;t[x].ls = ++tot, t[x].rs = ++tot;build(l,mid,t[x].ls), build(mid+1,r,t[x].rs), pushup(x);
}inline void pushdown(int x){ //基础的线段树标记下放if(t[x].lazy!=-1 && t[x].l != t[x].r){int ls = t[x].ls, rs = t[x].rs, lz = t[x].lazy;t[ls].lazy = lz, t[rs].lazy = lz;t[ls].lc = t[ls].rc = lz, t[rs].lc = t[rs].rc = lz;t[ls].cnt = t[rs].cnt = 1;t[x].lazy = -1;}
}void update(int l, int r, int x, int c){ //基础的线段树更新if(t[x].l >= l && t[x].r <= r){t[x].lazy = c, t[x].cnt = 1, t[x].lc = t[x].rc = c;return;}pushdown(x);int mid = (t[x].l+t[x].r)>>1;if(mid >= l) update(l,r,t[x].ls,c);if(mid < r) update(l,r,t[x].rs,c);pushup(x);
}int query(int l, int r, int x){     //基础的线段树查询// LOG2("l",l,"r",r);// LOG2("t[x].l",t[x].l,"t[x].r",t[x].r);if(t[x].l >= l && t[x].r <= r) return t[x].cnt;pushdown(x);int mid = (t[x].l+t[x].r)>>1, tp = 0;if(mid >= l && mid >= r) tp += query(l,r,t[x].ls);else if(mid < l && mid < r) tp += query(l,r,t[x].rs);else if(mid >= l && mid < r){tp += query(l,r,t[x].ls);tp += query(l,r,t[x].rs);if(t[t[x].ls].rc == t[t[x].rs].lc) tp--;}return tp;
}int query_color(int pos, int x){  //查询单点颜色if(t[x].l == pos) return t[x].lc;if(t[x].r == pos) return t[x].rc;if(pos >= t[x].l && pos <= t[x].r && t[x].cnt == 1) return t[x].lc;int mid = (t[x].l+t[x].r)>>1;if(pos <= mid) return query_color(pos,t[x].ls);else return query_color(pos,t[x].rs);
}inline int sum(int x, int y){  //将区间分为多条链,对于每条链直接查询int ret = 0;while(top[x] != top[y]){ //让x与y到达同一条链if(d[top[x]] < d[top[y]]) swap(x,y); //找到更深的点ret += query(id[top[x]],id[x],rt);// LOG1("ret",ret);if(query_color(id[top[x]],rt) == query_color(id[fa[top[x]]],rt)) ret--;x = fa[top[x]];}if(id[x] > id[y]) swap(x,y);return (ret+query(id[x],id[y],rt));
}inline void updates(int x, int y, int c){  //区间加z, 将区间分为多条链while(top[x] != top[y]){if(d[top[x]] < d[top[y]]) swap(x,y);update(id[top[x]],id[x],rt,c);  //对于每条链直接修改x = fa[top[x]];}if(id[x] > id[y]) swap(x,y);update(id[x],id[y],rt,c);
}signed main()
{scanf("%lld%lld",&n,&m);rep(i,1,n) scanf("%lld",&val[i]);init();rep(i,1,n-1){int x,y; scanf("%lld%lld",&x,&y);add(x,y), add(y,x);}scanf("%lld",&m);root = 1;tot = 0, dfs1(root), dfs2(root, root);tot = 0, build(1,n,rt = ++tot);rep(i,1,m){char op[10]; scanf("%s",op);int x,y,z;if(op[0] == 'Q'){scanf("%lld%lld",&x,&y);printf("%lld\n",sum(x,y));}else{scanf("%lld%lld%lld",&x,&y,&z);updates(x,y,z);}}return 0;
}

【BZOJ 2243 SDOI2011】染色【树链剖分】相关推荐

  1. 【BZOJ2243】[SDOI2011]染色 树链剖分+线段树

    [BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的 ...

  2. 洛谷 P2486 [SDOI2011]染色 树链剖分

    目录 题面 题目链接 题目描述 输入输出格式 输入格式 输出格式 输入输出样例 输入样例: 输出样例: 说明 思路 PushDown与Update Q AC代码 总结与拓展 题面 题目链接 P2486 ...

  3. BZOJ 2243: [SDOI2011]染色

    2243: [SDOI2011]染色 >原题链接< Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点 ...

  4. HDU 3966 POJ 3237 HYSBZ 2243 HRBUST 2064 树链剖分

    树链剖分是一个很固定的套路 一般用来解决树上两点之间的路径更改与查询 思想是将一棵树分成不想交的几条链 并且由于dfs的顺序性 给每条链上的点或边标的号必定是连着的 那么每两个点之间的路径都可以拆成几 ...

  5. BZOJ 4034 [HAOI2015]T2 树链剖分

    Description 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中 ...

  6. HYSBZ - 2243染色——树链剖分+线段树建树技巧

    [题目描述] HYSBZ - 2243染色 [题目分析] 我一直没有看清楚题,以为求的是路径上出现颜色的种类,然后就写了一个区间染色的线段树进行维护,过样例的时候才发现题读错了,人家要求的是路径上出现 ...

  7. BZOJ 4034: [HAOI2015]T2 树链剖分

    4034: [HAOI2015]T2 Description 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操 ...

  8. bzoj 4127: Abs(树链剖分+线段树)

    4127: Abs Time Limit: 40 Sec  Memory Limit: 256 MB Submit: 667  Solved: 225 [Submit][Status][Discuss ...

  9. BZOJ 2243 染色(树链剖分好题)

    2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MB Submit: 7971  Solved: 2990 [Submit][Stat ...

  10. BZOJ 2243 树链剖分

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2243 题意:中文题目 思路:树链剖分.首先考虑求区间颜色段数的问题, 我们可以用线段树维护 ...

最新文章

  1. 如果从Silverlight 读取本地GB2312格式的xml文件
  2. 从零开始带你一步一步使用 YOLOv3 测试自己的数据
  3. 网络编程之shutdown() 与 close()函数详解
  4. java虚拟机监控_Java虚拟机监控工具
  5. linux发送邮件的功能总结
  6. 如何用Chrome读懂网站监测Cookie
  7. u盘在磁盘管理可以显示 但是电脑中找不到_U盘无法识别怎么办?试试这种方法,没准还有救!...
  8. 14013.petalinux操作GPIO
  9. sql server 加密_列级SQL Server加密概述
  10. Linux基础_合并,归档,压缩,dump,编辑器
  11. python如何设置画布开始位置_如何设置亚马逊站内广告?亚马逊自动广告手动广告都在什么位置?...
  12. 思科和华为交换机常用命令对比学习
  13. javascript学习心得(1)replace
  14. php分页类怎么使用,PHP实现的分页类定义与用法示例
  15. 大数据_HDFS原理
  16. 电脑窗口切换常用的快捷键有哪些
  17. Linux源码解析--从开机加电到main函数
  18. 搭建单节点ELK日志收集
  19. TikTok选品有什么技巧?
  20. 2021年PAT乙级春季真题

热门文章

  1. Verilog设计中的锁存器
  2. 没有权限角色管理功能菜单加载
  3. 你要金婚?还是金色软件?
  4. 《隋唐演义》二:竞争对手的实力在不断增强
  5. linux efi分区安装grub2,GitHub - beatfan/UEFI_grub2: uefi 版本 grub2,可以引导多系统,以及linux的安装...
  6. 华为harmonyos官方微博账号,华为 EMUI 官方微信和微博更名为 HarmonyOS
  7. php+ci+db+debug,[PHP] Web Framework : CodeIgniter MySQL Database 使用教學
  8. js文件里获取路由 vue_纯js文件中,怎么使用vue的路由
  9. swagger 上传文件 参数_跟我一起学.NetCore之Swagger让前后端不再烦恼及界面自定义...
  10. 【HDU6286】2018(容斥)