[CodeChef-QTREE6]Query on a tree VI
题目大意:
给你一棵黑白树,每个点默认是白色,要求支持以下两种操作:
1.改变一个点的颜色;
2.除去连接不同颜色的点的边,求某个点连通块的大小。
思路:
对原树维护两个树链剖分,
一棵维护当点x为白色时,以它为根结点的白色的子树大小;
另一棵维护当点x为黑色时,以它为根结点的黑色的子树大小。(两者均不考虑x的实际颜色)
方便起见,这里我们用q表示同一个连通分量中,深度最浅的祖先。
询问一个点时,相当于在询问q的值。
修改一个点时,相当于在原来颜色的树剖上将x到q的路径上的所有点同时减去x同色子树的大小,然后在新的颜色的树剖上将x到q路径上的所有点同时加上x同色子树的大小。
如果对每个点都加/减显然不方便,因此我们可以用一些数据结构(线段树/树状数组)来维护差分。
注意这道题会卡常,要么对每一条链建线段树,要么就用树状数组。
接下来的问题是如何找到祖先q。
如果暴力往上找,时间复杂度是O(n)的,显然会TLE。
假如我们能够直接判断某一个树链上的点是否是同一种颜色,那就可以直接往上跳了。
如何直接判断?
考虑用0代表白色,1代表黑色,如果当这条链上所有点的权值和等于这条链上结点的个数,或等于0,那么肯定是同一种颜色的。
这样我们可以直接维护树上前缀和,询问的时候减一下就可以了。
最后还剩半条链没法跳的时候可以二分中间的结点。
#include<cstdio>
#include<cctype>
#include<vector>
inline int getint() {
char ch;
while(!isdigit(ch=getchar()));
int x=ch^'';
while(isdigit(ch=getchar())) x=(((x<<)+x)<<)+(ch^'');
return x;
}
const int V=;
std::vector<int> e[V];
inline void add_edge(const int &u,const int &v) {
e[u].push_back(v);
}
int par[V],size[V],son[V],top[V],id[V],dep[V],id2[V],n;
bool col[V];
void dfs1(const int &x,const int &p) {
par[x]=p;
size[x]=;
dep[x]=dep[p]+;
for(unsigned i=;i<e[x].size();i++) {
const int &y=e[x][i];
if(y==p) continue;
dfs1(y,x);
size[x]+=size[y];
if(size[y]>size[son[x]]) {
son[x]=y;
}
}
}
void dfs2(const int &x) {
id[x]=++n;
id2[n]=x;
top[x]=x==son[par[x]]?top[par[x]]:x;
if(son[x]) dfs2(son[x]);
for(unsigned i=;i<e[x].size();i++) {
const int &y=e[x][i];
if(y==par[x]||y==son[x]) continue;
dfs2(y);
}
}
class FenwickTree {
private:
int val[V];
int lowbit(const int &x) {
return x&-x;
}
public:
void modify(int p,const int &x) {
while(p<=n) {
val[p]+=x;
p+=lowbit(p);
}
}
int query(int p) {
int ret=;
while(p) {
ret+=val[p];
p-=lowbit(p);
}
return ret;
}
};
FenwickTree t[];
bool check(const int &u,const int &m) {
return t[].query(id[u])-t[].query(id[m]-)==col[u]*(dep[u]-dep[m]+);
}
inline int get_anc(int u) {
while(top[u]!=&&check(u,top[u])) {
if(col[par[top[u]]]==col[u]) {
u=par[top[u]];
} else {
return top[u];
}
}
int l=id[top[u]],r=id[u];
while(l<=r) {
const int mid=(l+r)>>;
if(check(u,id2[mid])) {
r=mid-;
} else {
l=mid+;
}
}
return id2[r+];
}
inline void modify2(int x,int y,const int &k,const bool &c) {
while(top[x]!=top[y]) {
t[c].modify(id[top[x]],k);
t[c].modify(id[x]+,-k);
x=par[top[x]];
}
t[c].modify(id[y],k);
t[c].modify(id[x]+,-k);
}
inline void modify(const int &u) {
if(u!=) modify2(par[u],par[get_anc(u)],-t[col[u]].query(id[u]),col[u]);
t[].modify(id[u],col[u]?-:);
col[u]^=true;
if(u!=) modify2(par[u],par[get_anc(u)],t[col[u]].query(id[u]),col[u]);
}
inline int query(const int &u) {
return t[col[u]].query(id[get_anc(u)]);
}
int main() {
int n=getint();
for(register int i=;i<n;i++) {
const int u=getint(),v=getint();
add_edge(u,v);
add_edge(v,u);
}
dfs1(,);
dfs2();
for(register int i=;i<=n;i++) {
t[].modify(id[i],size[i]);
t[].modify(id[i]+,-size[i]);
}
t[].modify(,);
for(register int m=getint();m;m--) {
const int t=getint(),u=getint();
if(t) {
modify(u);
} else {
printf("%d\n",query(u));
}
}
return ;
}
[CodeChef-QTREE6]Query on a tree VI的更多相关文章
- QTREE6 - Query on a tree VI 解题报告
QTREE6 - Query on a tree VI 题目描述 给你一棵\(n\)个点的树,编号\(1\)~\(n\).每个点可以是黑色,可以是白色.初始时所有点都是黑色.下面有两种操作请你操作给我 ...
- bzoj3637 CodeChef SPOJ - QTREE6 Query on a tree VI 题解
题意: 一棵n个节点的树,节点有黑白两种颜色,初始均为白色.两种操作:1.更改一个节点的颜色;2.询问一个节点所处的颜色相同的联通块的大小. 思路: 1.每个节点记录仅考虑其子树时,假设其为黑色时所处 ...
- SP16549 QTREE6 - Query on a tree VI LCT维护颜色联通块
\(\color{#0066ff}{ 题目描述 }\) 给你一棵n个点的树,编号1~n.每个点可以是黑色,可以是白色.初始时所有点都是黑色.下面有两种操作请你操作给我们看: 0 u:询问有多少个节点v ...
- SPOJ QTREE6 Query on a tree VI 树链剖分
题意: 给出一棵含有\(n(1 \leq n \leq 10^5)\)个节点的树,每个顶点只有两种颜色:黑色和白色. 一开始所有的点都是黑色,下面有两种共\(m(1 \leq n \leq 10^5) ...
- [QTree6]Query on a tree VI
Description: 给你一棵n个点的树,编号1~n.每个点可以是黑色,可以是白色.初始时所有点都是黑色.下面有两种操作请你操作给我们看: 0 u:询问有多少个节点v满足路径u到v上所有节点(包括 ...
- 洛谷SP16549 QTREE6 - Query on a tree VI(LCT)
洛谷题目传送门 思路分析 题意就是要维护同色连通块大小.要用LCT维护子树大小就不说了,可以看看蒟蒻的LCT总结. 至于连通块如何维护,首先肯定可以想到一个很naive的做法:直接维护同色连通块,每次 ...
- SPOJ 16549 - QTREE6 - Query on a tree VI 「一种维护树上颜色连通块的操作」
题意 有操作 $0$ $u$:询问有多少个节点 $v$ 满足路径 $u$ 到 $v$ 上所有节点(包括)都拥有相同的颜色$1$ $u$:翻转 $u$ 的颜色 题解 直接用一个 $LCT$ 去暴力删边连 ...
- SP16549 QTREE6 - Query on a tree VI(LCT)
题意翻译 题目描述 给你一棵n个点的树,编号1~n.每个点可以是黑色,可以是白色.初始时所有点都是黑色.下面有两种操作请你操作给我们看: 0 u:询问有多少个节点v满足路径u到v上所有节点(包括)都拥 ...
- 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 ...
- QTREE6&&7 - Query on a tree VI &&VII
树上连通块 不用具体距离,只询问连通块大小或者最大权值 可以类比Qtree5的方法,但是记录东西很多,例如子树有无0/1颜色等 一个trick,两个LCT分离颜色 每个颜色在边上. 仅保留连通块顶部不 ...
随机推荐
- 使用隐藏form表单下载文件,解决url方式下载,由于环境问题而限制url长度,满足不了所有的需求!
一 对于某些环境导出是直接用wiondow.href=url直接导出下载,有些业务需求,如员工档案等字段比较多的时候,全选导出就会引发异常,由于Nginx转发长度限制的问题, 如果运维不愿意改变环境, ...
- python面向对象——类
from:http://www.runoob.com/python3/python3-class.html Python3 面向对象 Python从设计之初就已经是一门面向对象的语言,正因为如此,在P ...
- 【Python】POST上传APK检测是否存在ZipperDown漏洞
前言 用POST的方式上传文件,检测APK是否存在ZipperDown漏洞. 代码 # authour:zzzhhh # 2018.08.08 # check ZipperDown # -*- cod ...
- 使用RegSetValueEx修改注册表时遇到的问题(转)
原文转自 http://blog.csdn.net/tracyzhongcf/article/details/4076870 1.今天在使用RegSetValueEx时发现一个问题: RegSetVa ...
- NuGet套件还原步骤(以vs2012为例)
下载别人的范例,出现由于Nuget套件不存在而无法启动时: 效果如下图: 步骤如下: 1.点击 项目->启用NuGet程序包还原 2.点击下图中的是 3.点击下图中的确定 4.效果如图: . 5 ...
- python基础--hashlib模块
hashlib模块用于加密操作,代替了md5和sha模块, 主要提供SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法. # -*- coding:utf-8 - ...
- node练习笔记
一.用http模块实现客户端 1. 这个错误的原因是:客户端http_client.js里面的端口和服务端里面的端口不一样 2.querystring.stringify 字符串转换成对象 q ...
- MongoDB中多表关联查询(
1.使用aggregate 查看表数据 db.getCollection('reports').aggregate([ { $lookup: { from: "process", ...
- Git简明教程二、开始进行版本管理
上一篇介绍了Git中的一些基本概念.本篇来实际看一看如何通过几个常用命令来快速上手Git,完成版本管理的日常操作(核心操作). 0. 准备工作 安装Git后,请先在你的电脑上新建或选择一个目录作为测试 ...
- R语言学习笔记:使用reshape2包实现整合与重构
R语言中提供了许多用来整合和重塑数据的强大方法. 整合 aggregate 重塑 reshape 在整合数据时,往往将多组观测值替换为根据这些观测计算的描述统计量. 在重塑数据时,则会通过修改数据的结 ...