【bzoj3637】Query on a tree VI

11 篇文章 0 订阅
11 篇文章 0 订阅

似乎是code chef上的题,不知为何bzoj也有。

题意:每个节点有颜色,刚开始可能是白色,每次修改一个点的颜色,或者询问一个点所在同色连通块的大小

此题考虑维护一个点只考虑其子树时,颜色为白色和黑色的连通块大小。

显然需要树链剖分+树状数组

然后询问就是跳到深度最小的跟它同色的祖先。

修改也是同理。

然后考虑怎么找到深度最小的同色祖先。

每次判断所在重链是否全为它的颜色。若是,就跳过,不是,就在链上二分查找位置。

时间复杂度o(nlogn^2)

#include <bits/stdc++.h>
#define gc getchar()
#define N 100009
#define inf 0x3f3f3f3f
#define ll long long
#define mid (l+r>>1)
using namespace std;
int n,first[N],number,m,fa[N],size[N],Mson[N],deep[N],top[N],cnt,dfn[N];
int bit[N][3],color[N],x,y,op,id[N];
//0:black 1:white
struct edge
{
	int to,next;
	void add(int x,int y)
	{
		to=y,next=first[x],first[x]=number;
	}
}e[N<<1];
int read()
{
	int x=1;
	char ch;
	while (ch=gc,ch<'0'||ch>'9') if (ch=='-') x=-1;
	int s=ch-'0';
	while (ch=gc,ch<='9'&&ch>='0') s=s*10+ch-'0';
	return s*x;
}
int lowbit(int x)
{
	return x&(-x);
}
void add(int x,int y,int k)
{
	for (;x<=n;x+=lowbit(x)) bit[x][k]+=y;
}
int qry(int x,int k,int ret=0)
{
	for (;x;x-=lowbit(x)) ret+=bit[x][k];
	return ret; 
}
void dfs(int x)
{
	size[x]=1;
	deep[x]=deep[fa[x]]+1;
	for (int i=first[x];i;i=e[i].next)
		if (e[i].to!=fa[x])
		{
			fa[e[i].to]=x;
			dfs(e[i].to);
			size[x]+=size[e[i].to];
			if (size[e[i].to]>size[Mson[x]]) Mson[x]=e[i].to;
		}
}
void Dfs(int x,int y)
{
	id[dfn[x]=++cnt]=x;
	top[x]=y;
	if (Mson[x]) Dfs(Mson[x],y);
	for (int i=first[x];i;i=e[i].next)
		if (e[i].to!=fa[x]&&e[i].to!=Mson[x]) Dfs(e[i].to,e[i].to); 
}
void ins(int x,int y,int z,int k)
{
	for (;top[x]!=top[y];x=fa[top[x]])
	{
		if (deep[top[x]]<deep[top[y]]) swap(x,y);
		add(dfn[top[x]],z,k),add(dfn[x]+1,-z,k);
	}
	if (deep[x]>deep[y]) swap(x,y);
	add(dfn[x],z,k),add(dfn[y]+1,-z,k);
}
int get_anc(int x)
{
	int c=color[x];
	while (top[x]!=1)
	{
		int q=qry(dfn[x],2)-qry(dfn[top[x]]-1,2);
		if (q==c*(dfn[x]-dfn[top[x]]+1))
		{
			if (color[fa[top[x]]]!=c) return top[x];
			else x=fa[top[x]];
		}
		else
		{
			int l=dfn[top[x]],r=dfn[x],ret=0;
			while (l<=r)
			{
				if (qry(r,2)-qry(mid-1,2)!=c*(r-mid+1))
					l=mid+1;
				else ret=mid,r=mid-1;
			}
			return id[ret];
		}
	}
	int l=dfn[1],r=dfn[x],ret=0;
	while (l<=r)
	{
		if (qry(r,2)-qry(mid-1,2)!=c*(r-mid+1))
			l=mid+1;
		else ret=mid,r=mid-1;
	}
	return id[ret];
}
int main()
{
	n=read();
	for (int i=1;i<n;i++)
		x=read(),y=read(),e[++number].add(x,y),e[++number].add(y,x);
	fa[1]=1;
	dfs(1);
	Dfs(1,1);
	for (int i=1;i<=n;i++)
		add(dfn[i],size[i],0),add(dfn[i]+1,-size[i],0);
	add(1,1,1);
	m=read();
	for (int i=1;i<=m;i++)
	{
		op=read(),x=read();
		if (op==0) printf("%d\n",qry(dfn[y=get_anc(x)],color[x]));
		else
		{
			if (x>1) ins(fa[x],fa[y=get_anc(x)],-qry(dfn[x],color[x]),color[x]);
			add(dfn[x],(color[x]^=1)?1:-1,2);
			if (x>1) ins(fa[x],fa[y=get_anc(x)],qry(dfn[x],color[x]),color[x]);
		}
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值