loj #2001. 「SDOI2017」树点涂色(LCT)

题目链接:https://loj.ac/problem/2001


关键在于操作1,发现是一个LCT的Access操作,每次Access,将断掉的儿子所在的子树答案全部+1,将连接到这个节点的子树答案全部-1,然后我们可以利用dfs序来进行更新,同时用线段树维护,操作2为线段树上的单点查询,操作3为线段树上的区间查询。


代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=100000+5;
struct Edge
{
	int to,next; 
}edge[MAXN*2]; 
int head[MAXN],tot,in[MAXN],out[MAXN],tag;
void addedge(int u,int v)
{
	edge[tot].to=v;
	edge[tot].next=head[u];
	head[u]=tot++;
}
void init()
{
	tot=0;tag=0;
	memset(head,-1,sizeof(head));
}
struct seg
{
	#define lson l,mid,rt<<1
	#define rson mid+1,r,rt<<1|1
	int mx[MAXN<<2],lazy[MAXN<<2];
	inline void push_up(int rt)
	{
		mx[rt]=max(mx[rt<<1],mx[rt<<1|1]);
	}
	inline void push_down(int rt)
	{
		if(lazy[rt])
		{
			mx[rt<<1]+=lazy[rt];
			lazy[rt<<1]+=lazy[rt];
			mx[rt<<1|1]+=lazy[rt];
			lazy[rt<<1|1]+=lazy[rt];
			lazy[rt]=0;
		}
	}
	void update(int L,int R,int val,int l,int r,int rt)
	{
		if(L<=l&&r<=R)
		{
			lazy[rt]+=val;
			mx[rt]+=val;
			return ;
		}
		int mid=(l+r)>>1;
		push_down(rt);
		if(mid>=L)
			update(L,R,val,lson);
		if(mid<R)
			update(L,R,val,rson);
		push_up(rt);
	}
	int query(int L,int R,int l,int r,int rt)
	{
		if(L<=l&&r<=R)
		{
			return mx[rt];
		}
		int mid=(l+r)>>1;
		int ret=0;
		push_down(rt);
		if(L<=mid)
		{
			ret=max(ret,query(L,R,lson));
		}
		if(mid<R)
		{
			ret=max(ret,query(L,R,rson));
		}
		return ret;
	}
}T;
namespace lca
{
	const int DEG=20;
	int fa[MAXN][DEG];//fa[i][j]表示结点i的第2^j个祖先 
	int deg[MAXN];//深度数组
	void BFS(int root)
	{
		queue<int>que;
		deg[root]=0;
		fa[root][0]=root;
		que.push(root);
		while(!que.empty())
		{
			int tmp=que.front();
			que.pop();
			for(int i=1;i<DEG;i++)
				fa[tmp][i]=fa[fa[tmp][i-1]][i-1];
			for(int i=head[tmp];i!=-1;i=edge[i].next) 
			{
				int v=edge[i].to;
				if(v==fa[tmp][0])continue;
				deg[v]=deg[tmp]+1;
				fa[v][0]=tmp;
				que.push(v);
			}
		}
	}
	int LCA(int u,int v) 
	{
		if(deg[u]>deg[v])swap(u,v);
		int hu=deg[u],hv=deg[v];
		int tu=u,tv=v;
		for(int det=hv-hu,i=0;det;det>>=1,i++)
			if(det&1)
				tv=fa[tv][i];
		if(tu==tv)return tu;
		for(int i=DEG-1;i>=0;i--)
		{
			if(fa[tu][i]==fa[tv][i])
				continue;
			tu=fa[tu][i];
			tv=fa[tv][i];
		}
		return fa[tu][0]; 
	}
}
int n,m;
namespace LCT
{
	int ch[MAXN][2],pre[MAXN];
	bool rt[MAXN];
	void Rotate(int x)
	{
		int y=pre[x],kind=ch[y][1]==x;
		ch[y][kind]=ch[x][!kind];
		pre[ch[y][kind]]=y;
		pre[x]=pre[y];
		pre[y]=x;
		ch[x][!kind]=y;
		if(rt[y])
			rt[y]=false,rt[x]=true;
		else
			ch[pre[x]][ch[pre[x]][1]==y]=x;
	}
	void Splay(int r)
	{
		while(!rt[r])
		{
			int f=pre[r],ff=pre[f];
			if(rt[f])
				Rotate(r);
			else if((ch[ff][1]==f)==(ch[f][1]==r))
				Rotate(f),Rotate(r);
			else
				Rotate(r),Rotate(r);
		}
	}
	int find(int x)
	{
		while(ch[x][0])
		{
			x=ch[x][0];
		}
		return x;
	}
	int Access(int x)
	{
		int y=0;
		for(;x;x=pre[y=x])
		{
			Splay(x);
			int p;
			if(ch[x][1])
			{
				p=find(ch[x][1]);
				T.update(in[p],out[p],1,1,n,1);
			}
			if(y)
			{
				p=find(y);
				T.update(in[p],out[p],-1,1,n,1);
			}
			rt[ch[x][1]]=true,rt[ch[x][1]=y]=false;
		}
		return y;
	}
	void init(int n)
	{
		for(int i=0;i<=n;i++)
		{
			rt[i]=true;
			ch[i][0]=ch[i][1]=0;
			pre[i]=0;
		}
	}
}
void dfs(int u)
{
	in[u]=++tag;
	for(int i=head[u];i!=-1;i=edge[i].next)
	{
		int v=edge[i].to;
		if(LCT::pre[v]!=0)
			continue;
		LCT::pre[v]=u;
		dfs(v);
	}
	out[u]=tag;
	T.update(in[u],out[u],1,1,n,1);
}
int main()
{
	//freopen("paint1.in","r",stdin);
	//freopen("out.out","w",stdout);
	init();
	scanf("%d%d",&n,&m);
	LCT::init(n);
	for(int i=1;i<n;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		addedge(u,v);
		addedge(v,u);
	}
	LCT::pre[1]=-1;
	dfs(1);
	LCT::pre[1]=0;
	lca::BFS(1);
	//cerr<<clock()<<endl;
	while(m--)
	{
		int op,x,y;
		scanf("%d",&op);
		if(op==1)
		{
			scanf("%d",&x);
			LCT::Access(x);
		}
		if(op==2)
		{
			scanf("%d%d",&x,&y);
			int p=lca::LCA(x,y);
			int a,b,c;
			a=T.query(in[x],in[x],1,n,1);
			b=T.query(in[y],in[y],1,n,1);
			c=T.query(in[p],in[p],1,n,1);
			printf("%d\n",a+b-2*c+1);
		}
		if(op==3)
		{
			scanf("%d",&x);
			//printf("%d %d\n",in[x],out[x]);
			//printf("%d\n",T.mx[1]);
			printf("%d\n",T.query(in[x],out[x],1,n,1));
		}
	}
	return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值