题意:

给出一棵含有\(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. Java 多线程概念

    1.为什么要使用多线程: 更多的处理器核心. 更快的响应时间. 更好的变成模型. 2.线程的优先级: 现代操作系统基本采用时分的形式调度运行的线程,操作系统会分出一个个的时间片,线程会分配到若干时间片 ...

  2. 字典(dict),增删改查,嵌套

    一丶字典 dict 用{}来表示  键值对数据  {key:value}  唯一性 键 都必须是可哈希的 不可变的数据类型就可以当做字典中的键 值 没有任何限制 二丶字典的增删改查 1.增 dic[k ...

  3. vuejs 学习旅程一

    来上海快一年了,一直在东钿金融工作着,这一年来主要负责公司前期的房产评估微信平台,公司IT部也是刚刚成立,成立IT部门不仅仅只是维护房产评估微信,而是要做一个互金理财平台.于是我一年来的主要工作是负责 ...

  4. 【简抄】h5 新增的几个背景属性和文本属性

    一.背景图像显示: ①background-size:规定背景图像的大小: 值:像素值.百分比.auto.cover.contain ②background-origin :规定背景图像的起始位置: ...

  5. SQL SERVER 2014 缺少Business Intelligence 解决办法

    SQL SERVER 2014安装完所有的数据库工具后,缺少开发工具 Business Intelligence   之解决办法. https://msdn.microsoft.com/en-us/l ...

  6. mac不限速下载百度网盘

    本文转载自:https://blog.csdn.net/u010837612/article/details/80029212 相信大家都比较困惑,百度网盘客户端限速后一般只有几十K的下载速度,Win ...

  7. IOSruntime : 运行时机制

    首先必须明白的: 1.是什么 1> runtime是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API 2> 平时编写的OC代码, 在程序运行过程中, 其实最 ...

  8. Windows使用MySQL数据库管理系统中文乱码问题

    声明:本文关于MySQL中文乱码问题的解决方案均基于Windows 10操作系统,如果是Linux系统会有较多不适用之处,请谨慎参考. 一.MySQL中文乱码情况 1. sqlDevelper远程登陆 ...

  9. Python中的绝对路径和相对路径

    大牛们应该对路径都很了解了,这篇文章主要给像我这样的入门小白普及常识用的,啊哈 下面的路径介绍针对windows,其他平台的暂时不是很了解. 在编写的py文件中打开文件的时候经常见到下面其中路径的表达 ...

  10. 【Python图像特征的音乐序列生成】第一阶段的任务分配

    从即日起到7月20号,项目成员进行了第一次任务分配. 赵同学A.岳同学.周同学,负责了图像数据的情感数据集制作,他们根据自己的经验,对图像进行了情绪提取. 赵同学B全权负责向量映射这一块的网络搭建. ...