【BZOJ 2243 SDOI2011】染色【树链剖分】
题意:
一颗 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】染色【树链剖分】相关推荐
- 【BZOJ2243】[SDOI2011]染色 树链剖分+线段树
[BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的 ...
- 洛谷 P2486 [SDOI2011]染色 树链剖分
目录 题面 题目链接 题目描述 输入输出格式 输入格式 输出格式 输入输出样例 输入样例: 输出样例: 说明 思路 PushDown与Update Q AC代码 总结与拓展 题面 题目链接 P2486 ...
- BZOJ 2243: [SDOI2011]染色
2243: [SDOI2011]染色 >原题链接< Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点 ...
- HDU 3966 POJ 3237 HYSBZ 2243 HRBUST 2064 树链剖分
树链剖分是一个很固定的套路 一般用来解决树上两点之间的路径更改与查询 思想是将一棵树分成不想交的几条链 并且由于dfs的顺序性 给每条链上的点或边标的号必定是连着的 那么每两个点之间的路径都可以拆成几 ...
- BZOJ 4034 [HAOI2015]T2 树链剖分
Description 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中 ...
- HYSBZ - 2243染色——树链剖分+线段树建树技巧
[题目描述] HYSBZ - 2243染色 [题目分析] 我一直没有看清楚题,以为求的是路径上出现颜色的种类,然后就写了一个区间染色的线段树进行维护,过样例的时候才发现题读错了,人家要求的是路径上出现 ...
- BZOJ 4034: [HAOI2015]T2 树链剖分
4034: [HAOI2015]T2 Description 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操 ...
- bzoj 4127: Abs(树链剖分+线段树)
4127: Abs Time Limit: 40 Sec Memory Limit: 256 MB Submit: 667 Solved: 225 [Submit][Status][Discuss ...
- BZOJ 2243 染色(树链剖分好题)
2243: [SDOI2011]染色 Time Limit: 20 Sec Memory Limit: 512 MB Submit: 7971 Solved: 2990 [Submit][Stat ...
- BZOJ 2243 树链剖分
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2243 题意:中文题目 思路:树链剖分.首先考虑求区间颜色段数的问题, 我们可以用线段树维护 ...
最新文章
- 如果从Silverlight 读取本地GB2312格式的xml文件
- 从零开始带你一步一步使用 YOLOv3 测试自己的数据
- 网络编程之shutdown() 与 close()函数详解
- java虚拟机监控_Java虚拟机监控工具
- linux发送邮件的功能总结
- 如何用Chrome读懂网站监测Cookie
- u盘在磁盘管理可以显示 但是电脑中找不到_U盘无法识别怎么办?试试这种方法,没准还有救!...
- 14013.petalinux操作GPIO
- sql server 加密_列级SQL Server加密概述
- Linux基础_合并,归档,压缩,dump,编辑器
- python如何设置画布开始位置_如何设置亚马逊站内广告?亚马逊自动广告手动广告都在什么位置?...
- 思科和华为交换机常用命令对比学习
- javascript学习心得(1)replace
- php分页类怎么使用,PHP实现的分页类定义与用法示例
- 大数据_HDFS原理
- 电脑窗口切换常用的快捷键有哪些
- Linux源码解析--从开机加电到main函数
- 搭建单节点ELK日志收集
- TikTok选品有什么技巧?
- 2021年PAT乙级春季真题
热门文章
- Verilog设计中的锁存器
- 没有权限角色管理功能菜单加载
- 你要金婚?还是金色软件?
- 《隋唐演义》二:竞争对手的实力在不断增强
- linux efi分区安装grub2,GitHub - beatfan/UEFI_grub2: uefi 版本 grub2,可以引导多系统,以及linux的安装...
- 华为harmonyos官方微博账号,华为 EMUI 官方微信和微博更名为 HarmonyOS
- php+ci+db+debug,[PHP] Web Framework : CodeIgniter MySQL Database 使用教學
- js文件里获取路由 vue_纯js文件中,怎么使用vue的路由
- swagger 上传文件 参数_跟我一起学.NetCore之Swagger让前后端不再烦恼及界面自定义...
- 【HDU6286】2018(容斥)