题意:

给出一棵含有\(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. svn检出项目报错

    首先,我在浏览器访问svn检出项目地址是正常的,那么应该就是svn缓存的问题 1.         右键点击本地副本,TortoiseSVN -> Settings -> Saved Da ...

  2. easyUI filebox限定文件大小

    转载自:https://www.2cto.com/kf/201701/574667.html 侵删  easyui1.5filebox控件中增加文件大小的验证规则 2017-01-07 09:22:0 ...

  3. 关于报错“More than one fragment with the name [spring_web] was found. This is not legal ...”的解决办法

    最近在搭建一个spring mvc 项目时遇到“More than one fragment with the name [spring_web] was found. This is not leg ...

  4. jquery解析xml,获取xml标签名

    先给一个简单的XML,结构如下 <?xml version="1.0" encoding="uft-8" ?> <msg> <ro ...

  5. IT部门域事件与业务分析

    IT event--->system--->IT dept |--------------->IT dept |--------------->system 域事件分类: 直接 ...

  6. (转)VC得到可用的串口列表

    //枚举串口 //参数:bEnablePort,哪个串口有效,bEnablePort[0]表示COM1,bEnablePort[n-1]表示COMn //返回值:有效的串口个数 int EnumAll ...

  7. Python+selenium之下载文件

    一.Firefox文件下载 Web容许我们设置默认的文件下载路劲,文件会自动下载并且存放在指定的目录下. from selenium import webdriver import os fp = w ...

  8. java 访问docker的环境

    1.   配置环境 新增 ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock root@ros ...

  9. 使用tensorflow object_detection API训练自己的数据遇到的问题及解决方法

    1.Windows下出现找不到object_detection包的问题. 解决方法 在Anaconda3\soft\Lib\site-packages新建一个pth文件,将PedestrianDete ...

  10. MYSQL内置函数总结

    时间转换为字符串: SELECT date_format(Date, '%Y-%m-%d %H:%i:%s' ) AS TimeFROMtable o convert函数转换为字符串的时候不存在类型为 ...