偶然看见了这题,觉得自己 QTREE、COT 什么的都没有刷过的真是弱爆了……

一道思路很巧妙的题,终于是在约大爷的耐心教导下会了,真是太感谢约大爷了。

这题显然是树链剖分,但是链上维护的东西很恶心。其核心思想是找到一个相连的最浅同色节点,那么我只要维护每个点的子树中与他相连的点的数量即可

用 f[c][u] 表示在 u 的子树中与 u 相连 (假设 u 无色) 且颜色为 c 的点数

查询直接算出与 u 相连的最浅同色节点 a,ans=f[c[u]][a]

考虑修改,我们发现每次 u 被反转,影响到的点是 father[u] 一直往上,直到根或一个异色点(PS. 最浅异色 a 的 f[ ][a] 也会被改),而且他们的 f[][] 都是加一个数或减一个数

(PS2. father[u] 的 f[][] 会被改两次,因为 u 的颜色变了,导致 f[0][father[u]]、f[1][father[u]] 都在变)

区间修改,单点查询,于是用树状树组搞搞救过了

至于找到一个相连的最浅同色节点,可用线段树——比如:三叉神经树的做法

也可以在每条链上暴力挂 3 个 map 神马的……

反正是好 YY de 啦~

 #include <cstdio>
#include <cstring>
const int sizeOfPoint=; inline int getint();
inline void putint(int); int n, m;
int f[sizeOfPoint], d[sizeOfPoint], s[sizeOfPoint];
int p[][sizeOfPoint];
int num, idx[sizeOfPoint], son[sizeOfPoint], top[sizeOfPoint];
bool c[sizeOfPoint]; struct node
{
int ll, rr;
bool lc, rc;
int len;
node * l, * r;
inline void maintain();
};
node * t;
node memory_node[sizeOfPoint<<], * port_node=memory_node;
inline node * newnode(int, int);
node * build(int, int);
void update(node * , int);
int query(node * , int, int); int sum[][sizeOfPoint];
inline int lowbit(int);
inline void update(int * , int, int, int);
inline int query(int * , int); struct edge {int point; edge * next;};
edge memory_edge[sizeOfPoint<<], * port_edge=memory_edge;
edge * e[sizeOfPoint];
inline edge * newedge(int, edge * );
inline void link(int, int);
inline int lg(int);
void dfs_tree(int);
void dfs_chain(int, int);
inline int anc(int, int);
inline void update(bool, int, int, int);
inline int query(int); int main()
{
n=getint();
for (int i=;i<n;i++)
{
int u=getint(), v=getint();
link(u, v);
} memset(d, 0xFF, sizeof(d)); d[]=;
dfs_tree();
dfs_chain(, );
t=build(, n);
for (int i=;i<=n;i++) update(sum[], idx[i], idx[i], s[i]-); m=getint();
for (int i=;i<=m;i++)
{
int o=getint(), u=getint(); if (o==)
{
int f=query(u);
putint(+query(sum[c[f]], idx[f]));
}
else if (u==) c[u]^=;
else
{
int s=query(sum[c[u]], idx[u])+;
c[u]^=;
update(t, idx[u]); if (c[u]==c[f[u]])
{
update(sum[!c[u]], idx[f[u]], idx[f[u]], -s);
int a=query(f[u]);
s=query(sum[c[u]], idx[u])+;
if (a==) update(c[u], f[u], a, s);
else update(c[u], f[u], f[a], s);
}
else
{
int a=query(f[u]);
if (a==) update(!c[u], f[u], a, -s);
else update(!c[u], f[u], f[a], -s);
s=query(sum[c[u]], idx[u])+;
update(sum[c[u]], idx[f[u]], idx[f[u]], s);
}
}
} return ;
}
inline int getint()
{
register int num=;
register char ch;
do ch=getchar(); while (ch<'' || ch>'');
do num=num*+ch-'', ch=getchar(); while (ch>='' && ch<='');
return num;
}
inline void putint(int num)
{
char stack[];
register int top=;
if (num==) stack[top=]='';
for ( ;num;num/=) stack[++top]=num%+'';
for ( ;top;top--) putchar(stack[top]);
putchar('\n');
} inline void node::maintain()
{
lc=l->lc; rc=r->rc;
len=r->len;
if (len==r->rr-r->ll+ && l->rc==r->lc) len+=l->len;
}
inline node * newnode(int ll, int rr)
{
node * ret=port_node++;
ret->ll=ll; ret->rr=rr;
ret->l=ret->r=NULL;
return ret;
}
node * build(int ll, int rr)
{
node * t=newnode(ll, rr);
if (ll==rr) t->lc=t->rc=c[ll], t->len=;
else
{
int m=(ll+rr)>>;
t->l=build(ll, m);
t->r=build(m+, rr);
t->maintain();
}
return t;
}
void update(node * t, int k)
{
if (t->ll==t->rr) t->lc=t->rc=c[t->ll];
else
{
int m=(t->ll+t->rr)>>;
if (k<=m) update(t->l, k);
else update(t->r, k);
t->maintain();
}
}
int query(node * t, int ql, int qr)
{
int ret=;
if (t->ll==ql && t->rr==qr) ret=t->len;
else
{
int m=(t->ll+t->rr)>>;
if (qr<=m) ret=query(t->l, ql, qr);
else if (ql>m) ret=query(t->r, ql, qr);
else
{
ret=query(t->r, m+, qr);
if (ret==qr-m && t->r->lc==t->l->rc) ret+=query(t->l, ql, m);
}
}
return ret;
} inline int lowbit(int x)
{
return x & -x;
}
inline void update(int * c, int l, int r, int v)
{
for ( ;l<=n;l+=lowbit(l)) c[l]+=v;
for (r++;r<=n;r+=lowbit(r)) c[r]-=v;
}
inline int query(int * c, int i)
{
int ret=;
for ( ;i;i-=lowbit(i)) ret+=c[i];
return ret;
} inline edge * newedge(int point, edge * next)
{
edge * ret=port_edge++;
ret->point=point; ret->next=next;
return ret;
}
inline void link(int u, int v)
{
e[u]=newedge(v, e[u]); e[v]=newedge(u, e[v]);
}
inline int lg(int u)
{
return !u?:-__builtin_clz(u);
}
void dfs_tree(int u)
{
s[u]=;
for (int i=;i<=lg(d[u]);i++) p[i][u]=p[i-][p[i-][u]];
for (edge * i=e[u];i;i=i->next) if (d[i->point]==-)
{
f[i->point]=u; d[i->point]=d[u]+;
dfs_tree(i->point);
s[u]+=s[i->point];
if (s[i->point]>s[son[u]])
son[u]=i->point;
}
}
void dfs_chain(int u, int top_u)
{
idx[u]=++num; top[u]=u;
if (son[u]) dfs_chain(son[u], top_u);
for (edge * i=e[u];i;i=i->next) if (!idx[i->point])
dfs_chain(i->point, i->point);
}
inline int anc(int u, int k)
{
for (int i=;i>=;i--)
if ((k>>i)&)
u=p[k][u];
return u;
}
inline void update(bool c, int u, int v, int s)
{
while (top[u]!=top[v])
{
update(sum[c], idx[top[u]], idx[u], s);
u=f[top[u]];
}
update(sum[c], idx[v], idx[u], s);
}
inline int query(int u)
{
for ( ; ; )
{
int l=query(t, idx[top[u]], idx[u]); if (l==d[top[u]]-d[u]+)
{
if (top[u]==) return ;
if (c[top[u]]==c[f[top[u]]]) u=f[top[u]];
else return top[u];
}
else
return anc(u, l-);
}
}

解锁新成就:rank last

[BZOJ 3637]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. BZOJ 3637: Query on a tree VI LCT_维护子树信息_点权转边权_好题

    非常喜欢这道题. 点权转边权,这样每次在切断一个点的所有儿子的时候只断掉一条边即可. Code: #include <cstring> #include <cstdio> #i ...

  3. QTREE6 - Query on a tree VI 解题报告

    QTREE6 - Query on a tree VI 题目描述 给你一棵\(n\)个点的树,编号\(1\)~\(n\).每个点可以是黑色,可以是白色.初始时所有点都是黑色.下面有两种操作请你操作给我 ...

  4. bzoj3637: Query on a tree VI

    Description You are given a tree (an acyclic undirected connected graph) with n nodes. The tree node ...

  5. BZOJ3637 Query on a tree VI(树链剖分+线段树)

    考虑对于每一个点维护子树内与其连通的点的信息.为了换色需要,记录每个点黑白两种情况下子树内连通块的大小. 查询时,找到深度最浅的同色祖先即可,这可以比较简单的树剖+线段树乱搞一下(似乎就是qtree3 ...

  6. SPOJ QTREE Query on a tree VI

    You are given a tree (an acyclic undirected connected graph) with n nodes. The tree nodes are number ...

  7. BZOJ 3639: Query on a tree VII

    Description 一棵树,支持三种操作,修改点权,修改颜色,问所有与他路径上颜色相同的点的最大权,包含这两个点. Sol LCT. 用LCT来维护重边,对于每个节点在建一个set用来维护轻边,这 ...

  8. QTREE6&&7 - Query on a tree VI &&VII

    树上连通块 不用具体距离,只询问连通块大小或者最大权值 可以类比Qtree5的方法,但是记录东西很多,例如子树有无0/1颜色等 一个trick,两个LCT分离颜色 每个颜色在边上. 仅保留连通块顶部不 ...

  9. 2019.02.17 spoj Query on a tree VI(链分治)

    传送门 题意简述:给你一棵nnn个黑白点的树,支持改一个点的颜色,询问跟某个点颜色相同的连通块大小. 思路: 还是链分治 233 记fi,0/1f_{i,0/1}fi,0/1​表示iii的所有颜色为0 ...

随机推荐

  1. ubuntu更新源(Package has no installation candidate 的问题)

    最近将公司的台式机安装了ubuntu16.04,安装之后,使用apt-get install openssh-server 发现出现:Package 'openssh-server' has no i ...

  2. CMD和AMD区别的概括

    CMD和AMD区别   AMD CMD 关于依赖的模块 提前执行(不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)), 延迟执行 关于依赖的位置 依赖前置 ...

  3. Linux内核分析—完成一个简单的时间片轮转多道程序内核代码

    ---恢复内容开始--- 20135125陈智威 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-10 ...

  4. linux命令:文件类型和扩展名

    在linux系统中,一切皆是文件.Linux文件类型常见的有:普通文件.目录文件.字符设备文件和块设备文件.数据接口文件,符号链接文件,数据传送文件等. 1. 普通文件 用 ls -lh 来查看某个文 ...

  5. vimperator setting records

    vimperator confugration files :highlight Hint color:#000;background:rgb(250,230,150);border-radius:4 ...

  6. React Native 的绑定 this

    在React Native开发中,如果使用ES6语法的话,最好绑定this.但是使用ES5语法的话不需要绑定this.因为ES5会autobinding. this所指的就是直至包含this指针的上层 ...

  7. bootstrap 之 xs,sm,md,lg && 主要颜色

    mobile – xs ( <768px ) tablet – sm ( 768~991px ) desktop – md ( 992~1170px ) large desktop – lg ( ...

  8. mysql用户名密码忘记了解决方法

    今天想用一下实验室服务器的mysql,发现不记得用户名密码了. 解决方法如下: 1. 保证服务器处于安全的状态,如果可以请拔掉网线...(不过我跳过了这一步,额) 2. 修改/etc/my.cnf文件 ...

  9. 页面上常用的一些小功能--QQ、回到顶部

    1.QQ <script charset="utf-8" type="text/javascript" src="http://wpa.b.qq ...

  10. 使用if else if else 统计计算

    package review20140419;/* * 统计一个班级的成绩,并统计优良中差和不及格同学个数以及求平均分 */public class Test2 {    //程序的入口    pub ...