[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分离颜色 每个颜色在边上. 仅保留连通块顶部不 ...
随机推荐
- 查看oracle实例名
select name from v$database; show parameter service_names;
- 使用Netcat进行攻击
https://www.freebuf.com/column/135007.html 在网上找到了一个开启了ftp服务的服务: http://static.vhdong.com/Upload/Temp ...
- Southwestern Europe Regional Contest 2015 题解
题目链接:http://codeforces.com/gym/101128 题目数7/10 Rank 34/209 A: 题意:给出一张n个点的有向图表示一家有n个员工的公司的隶属图,u->v表 ...
- jQuery基本筛选器-表单筛选器-关系筛选器
一.基本筛选器 :first // 第一个 :last // 最后一个 :eq(index)// 索引等于index的那个元素 :even // 匹配所有索引值为偶数的元素,从 0 开始计数 :odd ...
- linux创建新用户
服务器只用root账号会有风险,最好是每个人使用一个账号. 1. 添加用户名 adduser linuxidc 2. 给这个用户名设置新密码 passwd linuxidc 3.授权 个人用户的权限只 ...
- MySQL触发器Trigger实例篇
定义: 何为MySQL触发器? 在MySQL Server里面也就是对某一个表的一定的操作,触发某种条件(Insert,Update,Delete 等),从而自动执行的一段程序.从这种意义上讲触发器是 ...
- linux定时任务-cron
/sbin/service crond start //启动服务 /sbin/service crond stop //关闭服务 /sbin/service crond restart //重启服务 ...
- dede图集内容页调用
{dede:productimagelist} <li> <img src="[field:imgsrc/]" width="92" heig ...
- 关于UrlEncode 一团乱麻的问题,后续彻底理解。Java中的 URLEncoder 与 URLDecoder无bug
很多开放平台都是小白开发的,对这个urlencode理解的不到位,他们总是认为java官方的urlencode有bug,需要 URLEncoder.encode("Hello World&q ...
- python目录/文件操作
目录操作 sys.argv[0] # 获得当前脚本路径,即当前工作目录\脚本名 os.getcwd() # 获得当前工作目录 os.path.abspath('.') # 获得当前工作目录 os.pa ...