偶然看见了这题,觉得自己 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. JDK8+Dubbo2.5.2实践

    几年前就听说过Dubbo的大名,今天由于工作需要,研究一下. 从网上找了一篇文章,非常靠谱,并且提供了简单的示例代码,基本上可以跑起来. 文章地址: http://www.cnblogs.com/Ja ...

  2. STM32中断管理函数

    CM3 内核支持256 个中断,其中包含了16 个内核中断和240 个外部中断,并且具有256 级的可编程中断设置.但STM32 并没有使用CM3 内核的全部东西,而是只用了它的一部分. STM32 ...

  3. silverlight导出excel

    开发导出excel,首先需要添加项目引用. Microsoft.CSharp 这个是应用dynamic的前提. 在代码页,需要添加引用 using System.Runtime.InteropServ ...

  4. jQuery原生框架中的jQuery.fn.extend和jQuery.extend

    extend 方法在 jQuery 中是一个很重要的方法,jQuey 内部用它来扩展静态方法或实例方法,而且我们开发 jQuery 插件开发的时候也会用到它.但是在内部,是存在 jQuery.fn.e ...

  5. COMP9020

    outline: numbers, sets, functions week 1logic week 2–3relation theory week 5–6graphs and trees week ...

  6. RABBITMQ(小总结 持续更新...

    (一)理解消息通信 1.消息通信概念---消费者.生产者和代理 生产者(producer)创建消息,然后发送到代理服务器(RaabitMQ). 其中消息包括两部分内容:有效载荷(payload)和标签 ...

  7. Android Studio项目目录结构介绍——android菜鸟成长之路

    在Android Studio中,提供了以下几种项目结构类型 我们一般常用的有以下两种结构: Project 结构类型 app/build/ app模块build编译输出的目录 app/build.g ...

  8. Spring之实现任务调度

    现实生活中,我们经常会制定一些"任务"在什么时间完成什么事情.同样在各种的企业中也会遇到这种任务调度的需求,比如商家或者网站的月报表 之类的要在每个月的最后一天统计各种数据,备份每 ...

  9. Angularjs select的使用

    实例一:基本下拉效果 usage: label for value in array <!-- lang: html --> <select ng-model="selec ...

  10. 第六周——分析Linux内核创建一个新进程的过程

    "万子恵 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 &q ...