SPOJ QTREE3Query on a tree again!(树链剖分+树状数组+二分)

SPOJ QTREE3Query on a tree again!(树链剖分+树状数组+二分)

题目大意

现在有一颗树,其所有的节点都为白色。有两种操作 ,一是将一个节点的颜色进行反转(即黑色变成白色,白色变成黑色),一是查询1号节点和v节点路径上深度最小的那个黑色节点

解题思路

设黑色的节点权值为1,白色的节点权值为0,对树进行剖分后用树状数组维护前缀和。对于每次查询,先找出节点v在上升到1号节点的路径上经过的所有的链的片段,对第一条和不为0的链的片段进行二分找出深度最小的点即可

AC代码

#include<bits/stdc++.h>
using namespace std;
const int size=4e5+5;
int head[size],nxt[size*2];
int to[size*2];
int dfn[size],fa[size],top[size];
int c[size],sz[size],son[size],dep[size];
int tot,cnt;
int a[size],opi[size];
inline int lowbit(int x) {return x&(-x);}
void init()
{
	memset(head,-1,sizeof(head));
	tot=0,cnt=0;
	sz[0]=0,dep[0]=0;
	memset(c,0,sizeof(c));
	memset(a,0,sizeof(a));
}
void addedge(int u,int v)
{
	to[tot]=v;
	nxt[tot]=head[u];
	head[u]=tot++;
}
void dfs1(int v,int f=0)
{
	fa[v]=f;
	dep[v]=dep[f]+1;
	sz[v]=1;
	son[v]=0;
	for(int i=head[v];i!=-1;i=nxt[i])
	{
		if(to[i]==f) continue;
		dfs1(to[i],v);
		sz[v]=sz[to[i]]+sz[v];
		if(sz[son[v]]<sz[to[i]]) son[v]=to[i];
	}
}
void dfs2(int v,int f,int k)
{
	top[v]=k;
	dfn[v]=++cnt;
	opi[cnt]=v;
	if(son[v]!=0) dfs2(son[v],v,k);
	for(int i=head[v];i!=-1;i=nxt[i])
	{
		if(to[i]==son[v]||to[i]==f) continue;
		dfs2(to[i],v,to[i]);
	}
}
void add(int x,int v)
{
	for(int i=x;i<=cnt;i+=lowbit(i))
	{
		c[i]+=v;
	}
}
int qsum(int x)
{
	int ans=0;
	for(int i=x;i>0;i-=lowbit(i))
	{
		ans+=c[i];
	}
	return ans;
}
vector<int> vec;
int qbtree(int v)
{
	vec.clear();
	int tv=v;
	while(top[tv]!=1)
	{
		vec.push_back(top[tv]);
		tv=fa[top[tv]];
	}
	vec.push_back(1);
	int l=-1,r;
	int bef;
	for(int i=vec.size()-1;i>=0;i--)
	{
		if(i==0) bef=v;else bef=fa[vec[i-1]];
		if(qsum(dfn[bef])-qsum(dfn[vec[i]]-1)>0)
		{
			l=dfn[vec[i]],r=dfn[bef];
			break;
		}
	}
	if(l==-1) return -1;
	int basesum=qsum(l-1);
	int ans=r;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(qsum(mid)-basesum>0)
		{
			ans=mid;
			r=mid-1;
		}
		else l=mid+1;
	}
	return opi[ans];
}	
int main()
{
	int n,q;
	//freopen("b.in","r",stdin);
	while(~scanf("%d%d",&n,&q))
	{
		init();
		int u,v;
		for(int i=1;i<n;i++)
		{
			scanf("%d%d",&u,&v);
			addedge(u,v);
			addedge(v,u);
		}
		dfs1(1);
		dfs2(1,0,1);
		int op;int vx;
		while(q--)
		{
			scanf("%d%d",&op,&vx);
			if(op==0)
			{
				if(a[dfn[vx]]==0) add(dfn[vx],1),a[dfn[vx]]=1;
				else add(dfn[vx],-1),a[dfn[vx]]=0;
			}
			else 
			{
				printf("%d\n",qbtree(vx));
			}
		}
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值