树链剖分+线段树 HDOJ 4897 Little Devil I(小恶魔)
题目链接
题意:
给定一棵树,每条边有黑白两种颜色,初始都是白色,现在有三种操作:
1 u v:u到v路径(最短)上的边都取成相反的颜色
2 u v:u到v路径上相邻的边都取成相反的颜色(相邻即仅有一个节点在路径上)
3 u v:查询u到v路径上有多少个黑色边
思路:
对树进行树链剖分,分成重链和轻链,用两棵线段树W,L来维护。W维护树上在重链上的u和v之间的边的翻转情况(操作在线段树上的[pos[v],pos[u]]区间);L维护树上在重链上的u和v之间的相邻边的翻转情况。那么某一个点u与它父亲节点fa[u]的边的最终翻转情况为:W(pos[u], pos[u])(如果边是重链上的边),W(pos[u], pos[u])^L(pos[fa[u]], pos[fa[u]])(如果边是轻链)。对于1操作,只要简单的在W上维护就可以了。对于2操作,除了在L上操作,还要注意头和尾的特殊处理(因为对于重链内的点,不包括头尾,只在W上查询),也就是u的重链上的儿子son[u]以及u的链头p=belong[u]要在W上翻转一次,结合图可能更能理解。还有就是线段树的操作了。
另外:
u可能没有son[u],默认为虚点0,那么在线段树上需要加上一句话:if (l == r) return ;
#include <bits/stdc++.h>const int N = 1e5 + 5;//线段树
#define lson l, mid, o << 1
#define rson mid + 1, r, o << 1 | 1
struct Seg_Tree {int fp[N<<2], s[N<<2];void flip(int l, int r, int o) {s[o] = (r - l + 1) - s[o];fp[o] ^= 1;}void push_up(int o) {s[o] = s[o<<1] + s[o<<1|1];}void push_down(int l, int r, int o) {if (fp[o]) {int mid = l + r >> 1;flip (lson);flip (rson);fp[o] = 0;}}void build(int l, int r, int o) {fp[o] = s[o] = 0;if (l == r) {return ;}int mid = l + r >> 1;build (lson);build (rson);}void updata(int ql, int qr, int l, int r, int o) {if (ql <= l && r <= qr) {flip (l, r, o);return ;}if (l == r) return ; //!push_down (l, r, o);int mid = l + r >> 1;if (ql <= mid) updata (ql, qr, lson);if (qr > mid) updata (ql, qr, rson);push_up (o);}int query(int ql, int qr, int l, int r, int o) {if (ql <= l && r <= qr) {return s[o];}push_down (l, r, o);int mid = l + r >> 1, ret = 0;if (ql <= mid) ret += query (ql, qr, lson);if (qr > mid) ret += query (ql, qr, rson);push_up (o);return ret;}
}W, L;std::vector<int> edge[N];
int sz[N], dep[N], son[N], fa[N];
int pos[N], belong[N];
int loc;
int n;int query(int u, int v) {int p = belong[u], q = belong[v], ret = 0;while (p != q) {if (dep[p] < dep[q]) {std::swap (p, q);std::swap (u, v);}if (u != p) {ret += W.query (pos[son[p]], pos[u], 1, n, 1);}ret += (W.query (pos[p], pos[p], 1, n, 1) ^ L.query (pos[fa[p]], pos[fa[p]], 1, n, 1));u = fa[p];p = belong[u];}if (u == v) return ret;if (dep[u] < dep[v]) {std::swap (u, v);}ret += W.query (pos[son[v]], pos[u], 1, n, 1);return ret;
}void modify(int t, int u, int v) {int p = belong[u], q = belong[v];while (p != q) {if (dep[p] < dep[q]) {std::swap (p, q);std::swap (u, v);}if (t == 1) {W.updata (pos[p], pos[u], 1, n, 1);} else {L.updata (pos[p], pos[u], 1, n, 1);W.updata (pos[son[u]], pos[son[u]], 1, n, 1);W.updata (pos[p], pos[p], 1, n, 1);}u = fa[p];p = belong[u];}if (dep[u] < dep[v]) {std::swap (u, v);}if (t == 1) {if (u == v) return ;W.updata (pos[son[v]], pos[u], 1, n, 1);} else {L.updata (pos[v], pos[u], 1, n, 1);W.updata (pos[son[u]], pos[son[u]], 1, n, 1);W.updata (pos[v], pos[v], 1, n, 1);}
}//树链剖分
void DFS2(int u, int chain) {pos[u] = ++loc;belong[u] = chain;if (son[u]) {DFS2 (son[u], chain);}for (auto v: edge[u]) {if (v == fa[u] || v == son[u]) continue;DFS2 (v, v);}
}void DFS1(int u, int pa) {sz[u] = 1; dep[u] = dep[pa] + 1;son[u] = 0; fa[u] = pa;for (auto v: edge[u]) {if (v == pa) continue;DFS1 (v, u);sz[u] += sz[v];if (sz[son[u]] < sz[v]) son[u] = v;}
}void prepare() {sz[0] = dep[0] = fa[0] = 0;DFS1 (1, 0);loc = 0;DFS2 (1, 1);W.build (1, n, 1);L.build (1, n, 1);
}void init_edge(int n) {for (int i=1; i<=n; ++i) {edge[i].clear ();}
}int main() {int T;scanf ("%d", &T);while (T--) {scanf ("%d", &n);init_edge (n);for (int i=1; i<n; ++i) {int u, v;scanf ("%d%d", &u, &v);edge[u].push_back (v);edge[v].push_back (u);}prepare ();int q;scanf ("%d", &q);while (q--) {int t, u, v;scanf ("%d%d%d", &t, &u, &v);if (t == 3) {printf ("%d\n", query (u, v));} else {modify (t, u, v);}}}return 0;
}
转载于:https://www.cnblogs.com/Running-Time/p/5669064.html
树链剖分+线段树 HDOJ 4897 Little Devil I(小恶魔)相关推荐
- 【BZOJ-2325】道馆之战 树链剖分 + 线段树
2325: [ZJOI2011]道馆之战 Time Limit: 40 Sec Memory Limit: 256 MB Submit: 1153 Solved: 421 [Submit][Sta ...
- BZOJ3862Little Devil I——树链剖分+线段树
题目大意: 给一棵树,每条边可能是黑色或白色(起始都是白色),有三种操作: 1.将u到v路径上所有边颜色翻转(黑->白,白->黑) 2.将只有一个点在u到v路径上的边颜色翻转 3.查询u到 ...
- CodeForces - 160D Edges in MST(思维+tarjan/树链剖分+线段树)
题目链接:点击查看 题目大意:给出一张 n 个点 m 条边组成的带权无向图,现在对于每条边来说,确定一下其分类: 一定是最小生成树上的边 可能是最小生成树上的边 一定不是最小生成树的边 题目分析:两种 ...
- CodeForces - 609E Minimum spanning tree for each edge(最小生成树+树链剖分+线段树/树上倍增)
题目链接:点击查看 题目大意:给出一张 n 个点和 m 条边组成的无向图,现在询问包含每一条边的最小生成树 题目分析:考虑求解次小生成树的思路: 求出最小生成树 ans 枚举每一条非树边 ( u , ...
- P2486 [SDOI2011]染色(树链剖分+线段树)
题干描述 输入描述 输出格式 对于每个询问操作,输出一行答案. 输入输出样例 输入 #1 复制 6 5 2 2 1 2 1 1 1 2 1 3 2 4 2 5 2 6 Q 3 5 C 2 1 1 Q ...
- BZOJ4127Abs——树链剖分+线段树
题目描述 给定一棵树,设计数据结构支持以下操作 1 u v d 表示将路径 (u,v) 加d 2 u v 表示询问路径 (u,v) 上点权绝对值的和 输入 第一行两个整数n和m,表示结点个数和操作数 ...
- 【BZOJ2243】[SDOI2011]染色 树链剖分+线段树
[BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的 ...
- HDU 2460 Network(双连通+树链剖分+线段树)
HDU 2460 Network 题目链接 题意:给定一个无向图,问每次增加一条边,问个图中还剩多少桥 思路:先双连通缩点,然后形成一棵树,每次增加一条边,相当于询问这两点路径上有多少条边,这个用树链 ...
- BZOJ2325[ZJOI2011]道馆之战——树链剖分+线段树
题目描述 口袋妖怪(又名神奇宝贝或宠物小精灵)红/蓝/绿宝石中的水系道馆需要经过三个冰地才能到达馆主的面前,冰地中 的每一个冰块都只能经过一次.当一个冰地上的所有冰块都被经过之后,到下一个冰地的楼梯才 ...
- YbtOJ-染色计划【树链剖分,线段树,tarjan】
正题 题目大意 给出nnn个点的一棵树,每个点有个颜色aia_iai,你每次可以选择一个颜色全部变成另一个颜色. 求最少多少次操作可以把一种颜色变成一个完整的连通块. 1≤k≤n≤2×1051\le ...
最新文章
- 浅析枚举类型(Enumerated types)
- JS的对象及其属性和方法
- 遍历Map key-value的两种方法、遍历Set方法
- Java forEach() 方法的三种用法
- quartus仿真18:仿真简单D触发器和JK触发器输出的波形
- Linux进程状态(ps stat)之R、S、D、T、Z、X 转:http://blog.csdn.net/huzia/article/details/18946491...
- linux shell awk -F‘:‘ ‘{print $1}‘
- 23种设计模式总结+清晰图解(必收藏)
- sqlserver 执行计划
- 苹果6s强制删除id锁_苹果ID锁安全神话破灭!2分钟就能解锁
- java170道面试题汇总+详细解析
- java rd th 小票机_英语日期rd,th有什么区别,怎么运用
- React中文文档之Conditional Rendering
- 亚特兰提斯之人鱼宝宝
- Unirech:为什么不能访问阿里云国际版云服务器ecs实例上的网站
- 数据集-20个免费的数据源/网站
- matlab中将数据保存为txt文件_matlab中将数据输出保存为txt格式文件的方式
- 简易防火墙建置与流量统计
- $我不是韭菜--基金篇(力哥P1-P6)$
- 卡拉兹猜想证明用python_科普:数学领域中的感觉像是“民科”的卡拉兹猜想