题意:

给出一棵含有\(n(1 \leq n \leq 10^5)\)个节点的树,每个顶点只有两种颜色:黑色和白色。

一开始所有的点都是黑色,下面有两种共\(m(1 \leq n \leq 10^5)\)次操作:

  • \(0 \, u\)表示查询\(u\)所在的连通块的大小,相邻两个点颜色相同则属于一个连通块。
  • \(0 \, u\)表示翻转\(u\)的颜色,即黑点变白点,白点变黑点。

分析:

参考CodeChef上的题解

首先将这棵树剖分成轻重链。

然后我们维护两个值:\(White(u)\)和\(Black(u)\)。

\(White(u)\)表示当\(u\)是白点时(这里我们不关心\(u\)真正的颜色),以\(u\)为根的子树中,\(u\)所在的连通块的大小。

同理,\(Black(u)\)表示\(u\)是黑点时(这里我们不关心\(u\)真正的颜色),以\(u\)为根的子树中,\(u\)所在的连通块的大小。

  • 对于查询操作\(0 \, \, u\):

    从\(u\)往上走,走到深度最小的与\(u\)同色的节点\(v\),那么答案就是\(White(v)\)或\(Black(v)\)。

  • 对于修改操作\(1, \, \, u\):

    由于对称性,不妨假设\(u\)从白点变为黑点。

    从\(u\)的父节点往上走,走到第一个黑点\(v_1\),设路径\(path_1\)为\(fa(u) \to v\),然后将\(path_1\)上所有点的\(White\)值减去\(White(u)\)。

    同样地,从\(u\)的父节点往上走,走到第一个白点\(v_2\),设路径\(path_2\)为\(fa(u) \to v_2\),然后将\(path_2\)上所有点的\(Black\)值加上\(Black(u)\)。

    这里修改操作是成段更新的,所以要用线段树维护一下。

接下来还要解决一个问题:如何快速找到上面说的深度最浅的同色点和遇到的第一个黑/白点

继续用线段树维护一个\(fir0\)和\(fir1\),表示该区间从右往左遇到的第一个白点和黑点,对应到树上的链就是从下往上。

这样就解决了第二个问题,其实第一个问题的答案也可以间接得到。

要找深度最前的同色点,就是第一个异色点的那个子节点。

父节点只有一个,但子节点又如何确定呢?

注意到,我们查询时是在剖出来的链上一条一条往上“跳”的,所以在同一条链上一个点的子节点就在线段树中它位置的右边相邻的那个点。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std; const int maxn = 100000 + 10;
const int maxnode = maxn * 4; struct Edge
{
int v, nxt;
Edge() {}
Edge(int v, int nxt): v(v), nxt(nxt) {}
}; int ecnt, head[maxn];
Edge edges[maxn * 2]; void AddEdge(int u, int v) {
edges[ecnt] = Edge(v, head[u]);
head[u] = ecnt++;
} int n; int fa[maxn], sz[maxn], son[maxn], dep[maxn]; void dfs(int u) {
sz[u] = 1; son[u] = 0;
for(int i = head[u]; ~i; i = edges[i].nxt) {
int v = edges[i].v;
if(v == fa[u]) continue;
fa[v] = u;
dep[v] = dep[u] + 1;
dfs(v);
sz[u] += sz[v];
if(sz[v] > sz[son[u]]) son[u] = v;
}
} int tot, top[maxn], id[maxn], pos[maxn]; void dfs2(int u, int tp) {
top[u] = tp;
id[u] = ++tot;
pos[tot] = u;
if(!son[u]) return;
dfs2(son[u], tp);
for(int i = head[u]; ~i; i = edges[i].nxt) {
int v = edges[i].v;
if(v == fa[u] || v == son[u]) continue;
dfs2(v, v);
}
} int color[maxn], addv[2][maxnode];
int fir[2][maxnode]; void build(int o, int L, int R) {
if(L == R) {
fir[0][o] = 0; fir[1][o] = L;
addv[0][o] = 1; addv[1][o] = sz[pos[L]];
return;
}
int M = (L + R) / 2;
build(o<<1, L, M);
build(o<<1|1, M+1, R); fir[0][o] = 0;
fir[1][o] = R;
} void pushdown(int o) {
for(int i = 0; i < 2; i++) {
int& t = addv[i][o];
if(!t) continue;
addv[i][o<<1] += t;
addv[i][o<<1|1] += t;
t = 0;
}
} void update(int o, int L, int R, int qL, int qR, int col, int v) {
if(qL <= L && R <= qR) {
addv[col][o] += v;
return;
}
pushdown(o);
int M = (L + R) / 2;
if(qL <= M) update(o<<1, L, M, qL, qR, col, v);
if(qR > M) update(o<<1|1, M+1, R, qL, qR, col, v);
} void UPDATE(int u, int v, int col, int val) {
while(top[u] != top[v]) {
update(1, 1, n, id[top[u]], id[u], col, val);
u = fa[top[u]];
}
update(1, 1, n, id[v], id[u], col, val);
} int querysize(int o, int L, int R, int p, int col) {
if(L == R) return addv[col][o];
pushdown(o);
int M = (L + R) / 2;
if(p <= M) return querysize(o<<1, L, M, p, col);
else return querysize(o<<1|1, M+1, R, p, col);
} int queryfir(int o, int L, int R, int qL, int qR, int col) {
if(qL <= L && R <= qR) return fir[col][o];
int ans = 0;
int M = (L + R) / 2;
if(qR > M) ans = queryfir(o<<1|1, M+1, R, qL, qR, col);
if(ans) return ans;
if(qL <= M) ans = queryfir(o<<1, L, M, qL, qR, col);
return ans;
} int QueryFir(int u, int col) {
int ans = 0;
int t = top[u];
while(t != 1) {
ans = queryfir(1, 1, n, id[t], id[u], col);
if(ans) return ans;
u = fa[t]; t = top[u];
}
return queryfir(1, 1, n, 1, id[u], col); } int QuerySuf(int u, int col) {
int ans = id[u];
while(top[u] != 1) {
int t = queryfir(1, 1, n, id[top[u]], id[u], col ^ 1);
if(t) return t == id[u] ? ans : t + 1;
ans = id[top[u]];
u = fa[top[u]];
}
int t = queryfir(1, 1, n, 1, id[u], col ^ 1);
if(!t) return 1;
return t == id[u] ? ans : t + 1;
} void change(int o, int L, int R, int p) {
if(L == R) {
int u = pos[L];
int& c = color[u];
c ^= 1;
fir[c][o] = L;
fir[c ^ 1][o] = 0;
return;
}
int M = (L + R) / 2, lenr = R - M;
if(p <= M) change(o<<1, L, M, p);
else change(o<<1|1, M+1, R, p);
fir[0][o] = fir[0][o<<1|1] ? fir[0][o<<1|1] : fir[0][o<<1];
fir[1][o] = fir[1][o<<1|1] ? fir[1][o<<1|1] : fir[1][o<<1];
} int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++) color[i] = 1; ecnt = 0;
memset(head, -1, sizeof(head));
for(int i = 1; i < n; i++) {
int u, v; scanf("%d%d", &u, &v);
AddEdge(u, v);
AddEdge(v, u);
} dfs(1);
tot = 0;
dfs2(1, 1); build(1, 1, n);
int _; scanf("%d", &_);
while(_--) {
int op, u; scanf("%d%d", &op, &u);
if(op == 0) {
int v = pos[QuerySuf(u, color[u])];
int ans = querysize(1, 1, n, id[v], color[v]);
printf("%d\n", ans);
} else {
if(u != 1) {
int v = pos[QueryFir(fa[u], color[u] ^ 1)];
if(!v) v = 1;
int sub = querysize(1, 1, n, id[u], color[u]);
UPDATE(fa[u], v, color[u], -sub); v = pos[QueryFir(fa[u], color[u])];
if(!v) v = 1;
int add = querysize(1, 1, n, id[u], color[u] ^ 1);
UPDATE(fa[u], v, color[u] ^ 1, add);
} change(1, 1, n, id[u]);
}
} return 0;
}

SPOJ QTREE6 Query on a tree VI 树链剖分的更多相关文章

  1. bzoj 3637: Query on a tree VI 树链剖分 && AC600

    3637: Query on a tree VI Time Limit: 8 Sec  Memory Limit: 1024 MBSubmit: 206  Solved: 38[Submit][Sta ...

  2. QTREE3 spoj 2798. Query on a tree again! 树链剖分+线段树

    Query on a tree again! 给出一棵树,树节点的颜色初始时为白色,有两种操作: 0.把节点x的颜色置反(黑变白,白变黑). 1.询问节点1到节点x的路径上第一个黑色节点的编号. 分析 ...

  3. spoj 375 Query on a tree(树链剖分,线段树)

      Query on a tree Time Limit: 851MS   Memory Limit: 1572864KB   64bit IO Format: %lld & %llu Sub ...

  4. SPOJ 375 Query on a tree(树链剖分)(QTREE)

    You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, ...

  5. SPOJ QTREE - Query on a tree 【树链剖分模板】

    题目链接 引用到的大佬博客 代码来自:http://blog.csdn.net/jinglinxiao/article/details/72940746 具体算法讲解来自:http://blog.si ...

  6. SPOJ 375 Query on a tree(树链剖分)

    https://vjudge.net/problem/SPOJ-QTREE 题意: 给出一棵树,树上的每一条边都有权值,现在有查询和更改操作,如果是查询,则要输出u和v之间的最大权值. 思路: 树链剖 ...

  7. SPOJ QTREE Query on a Tree【树链剖分模板题】

    树链剖分,线段树维护~ #include <cstdio> #include <cstring> #include <iostream> #include < ...

  8. SPOJ 375. Query on a tree (树链剖分)

    Query on a tree Time Limit: 5000ms Memory Limit: 262144KB   This problem will be judged on SPOJ. Ori ...

  9. SPOJ 375 Query on a tree【树链剖分】

    题目大意:给你一棵树,有两个操作1.修改一条边的值,2.询问从x到y路径上边的最大值 思路:如果树退化成一条链的话线段树就很明显了,然后这题就是套了个树连剖分,调了很久终于调出来第一个模板了 #inc ...

随机推荐

  1. SQL Server事务的四种隔离级别

    在SQL标准中定义了四种隔离级别,每一种级别都规定了一个事务中所做的修改,哪些是在事务内和事务间可见的,哪些是不可见的.较低级别的隔离通常可以执行更高的并发,系统的开销也更低. 1.未提交读(Read ...

  2. 微信小程序干货

    1.获取text文本框输入的信息 wxml代码 <view class="weui-cells"> <view class="weui-cell weu ...

  3. 关于vue-resource 转变成axios的过程

    在做东钿贷后系统的时候,我选择了vue-resource这个插件作为与服务器沟通工具,但是听说前端同行说vuejs2.0已经不在维护vue-resource了,vuejs2.0 已经使用了axios了 ...

  4. [Loading Component]Loading组件的v-model设计是否不合理?

    vue在2.4.2版本中给computed里的属性加了限制,详见assigning to a computed property without setter does not fail 项目将vue ...

  5. 【css】css2实现两列三列布局的方法

    前言 对于 flex 弹性布局相信大家都有所了解,它是 css3 中的属性,然而它具有一定的兼容性问题.楼主前几天面试时遇到了面试官需要设计一个两列布局,我当然就说父元素 flex 吧哩吧啦,然而需要 ...

  6. vim的命令

    下面是从一个博客里摘抄出来的, 供自己学习使用.   在命令状态下对当前行用== (连按=两次), 或对多行用n==(n是自然数)表示自动缩进从当前行起的下面n行.你可以试试把代码缩进任意打乱再用n= ...

  7. linux配置tomcat已service方式启动

    1. 在/etc/init.d目录下新建文件,命名为tomcat2. 对tomcat文件进行编辑,执行 # cd /etc/init.d/ # vi tomcat 将下面代码粘上去 注意:下面代码ja ...

  8. python爬虫之路——初识数据库存储

    非关系型数据库:MongoDB.关系型数据库:MySQL 关系型和非关系型的区别: 安装: 使用: 应用场景: mongoDB是一种非关系型数据库,分为四大类:键值存储数据库,列存储数据库,文档型数据 ...

  9. appium---AndroidSdk安装

    AndroidSDK指的是Android专属的软件开发工具包,被软件开发工程师用于为特定的软件包.软件框架.硬件平台.操作系统等建立应用软件的开发工具的集合.Android又是采用java语言进行开发 ...

  10. 《毛毛虫团队》第八次团队作业:ALPHA冲刺

    一:实验名称:软件测试与ALPHA冲刺 二:实验目的与要求 (1)掌握软件测试基础技术. (2)学习迭代式增量软件开发过程(Scrum). 三:实验步骤 任务一:各个成员今日完成的任务: 任务二:明日 ...