题意:

给出一棵含有\(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. javascript 关于hashtable

    javascript 实现HashTable(哈希表) 一.javascript哈希表简介 javascript里面是没有哈希表的,一直在java,C#中有时候用到了这一种数据结构,javascrip ...

  2. Hi,bro

    这是我第一次写部落格,也是我刚开始学python,希望我以后能把To Do List 做好,也希望大家可以好好学习,为了以后good life去努力,Do SomeThing OK?

  3. God made relatives.Thank God we can choose our friends.

    God made relatives.Thank God we can choose our friends. 神决定了谁是你的亲戚, 幸运的是在选择朋友方面他给了你留了余地

  4. redis 大批量数据插入导致MISCONF Redis is configured to save RDB snapshots的解决

    PS:之前写过一遍,那个方法没有彻底解决,现找到真正的解决方法 环境:redis 3.2.100 windows版(注意!!!这是关键),win10,redis客户端spring boot 2.0.7 ...

  5. web APP到底和跨平台APP开发有什么区别?

    什么是web app? WebApp是指基于Web的系统和应用,其作用是向广大的最终用户发布一组复杂的内容和功能. 说的浅显易懂点儿就是因为移动互联网特别火爆,很多企业公司也都想拥有一个属于自己的ap ...

  6. Lua与游戏的不解之缘

    本文转载自秦元培博客:blog.csdn.net/qinyuanpei 一.什么是Lua? Lua 是一个小巧的脚本语言,巴西里约热内卢天主教大学里的一个研究小组于1993年开发,其设计目的是为了嵌入 ...

  7. 用dfs求解八皇后问题

    相信大家都已经很熟悉八皇后问题了,就是指:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同一列或同一斜线上,问有多少种摆法.主要思路:按行进行深度优先搜索,在该 ...

  8. Sublime 安装Boxy + OmniMarkupPreviewer

    Sublime 安装Boxy + OmniMarkupPreviewer Package Install 安装 网络安装 ctrl+反引号打开控制台,在控制台中输入代码 import urllib.r ...

  9. MySql查询时间段的方法

    本文实例讲述了MySql查询时间段的方法.分享给大家供大家参考.具体方法如下: MySql查询时间段的方法未必人人都会,下面为您介绍两种MySql查询时间段的方法,供大家参考. MySql的时间字段有 ...

  10. C11 C语言文件的读写

    目录 文件的打开和关闭 字符流读写文件 文件的打开和关闭 fopen( ) fopen( ) 函数来创建一个新的文件或者打开一个已有的文件,这个调用会初始化类型 FILE 的一个对象,类型 FILE ...