洛谷P1501 lct,懒标记

题意:

给出一个 n n n个结点的树,初始权值为1,处理 q q q个操作,操作只会是以下四种之一

(1) +   x   y   k +\ x\ y\ k + x y k x x x y y y的路径上的每个点的权值 + k +k +k

(2) ∗   x   y   k *\ x\ y\ k  x y k x x x y y y的路径上的每个点的权值 ∗ k *k k

(3) −   x 1   y 1   x 2   y 2 -\ x_{1}\ y_{1}\ x_{2}\ y_{2}  x1 y1 x2 y2,删去边 ( x 1 , y 1 ) (x_{1},y_{1}) (x1,y1),加入边 ( x 2 , y 2 ) (x_{2},y_{2}) (x2,y2)

(4) /   x   y /\ x\ y / x y,查询 x x x y y y路径上的点的权值和,模51061

方法:

删/连边显然是lct,但权值修改暴力修改超时了一发,需要用懒标记,线段树怎么用在lct就怎么用, s u m sum sum成员维护当前 s p l a y splay splay的总和,所以和线段树一样,需要维护元素个数,线段树可以 r − l + 1 r-l+1 rl+1得到,这里需要多加一个 s i z e size size来维护,懒标记直接 s p l i t ( x , y ) split(x,y) split(x,y)后放在 y y y上即可

懒标记优先级高的先处理,注意 p u s h d o w n pushdown pushdown之后清空懒标记

对于询问,我们不需要再次下放懒标记了,因为 s p l i t split split的时候先取出了首尾为 x x x y y y s p l a y splay splay,然后再一次 s p l a y ( y ) splay(y) splay(y) s p l a y splay splay前会下放懒标记,所以 s p l i t split split完直接查询 t r e e [ y ] . s u m tree[y].sum tree[y].sum即可。

另外,对于题目保证合法的 l i n k / c u t link/cut link/cut操作,我们不要再在判断时调用 f i n d r o o t findroot findroot了,在这里常数过大会超时。

#include<bits/stdc++.h>
#define ll long long
#define int long long
using namespace std;

const int mod=51061;

struct LCT
{
	struct node
	{
		ll v,sum,multitag,addtag; 
		int tag,son[2],fa,size;
	};
	stack<int>s;
	vector<node>tree;
	LCT():tree(100005,{0,0,1,0,0,{0,0},0}){}
	bool isroot(int x)
	{
		return tree[tree[x].fa].son[0]!=x&&tree[tree[x].fa].son[1]!=x;
	}
	int getson(int x,int y)
	{
		return x==tree[y].son[1];
	}
	void push_up(int x)
	{
		tree[x].size=1;
		tree[x].sum=tree[x].v;
		for(int i=0;i<=1;i++)
		{
			tree[x].sum=(tree[x].sum+tree[tree[x].son[i]].sum)%mod;
			tree[x].size+=tree[tree[x].son[i]].size;
		}
	}
	void rotate(int x)
	{
		int y=tree[x].fa,z=tree[y].fa;
		int k1=getson(x,y),k2=getson(y,z);
		tree[x].fa=z;
		if(!isroot(y)) tree[z].son[k2]=x;
		tree[y].son[k1]=tree[x].son[!k1];
		if(tree[x].son[!k1]) tree[tree[x].son[!k1]].fa=y;
		tree[x].son[!k1]=y;
		tree[y].fa=x;
		push_up(y); push_up(x);
	}
	void reverse(int x)
	{
		swap(tree[x].son[0],tree[x].son[1]);
		tree[x].tag^=1;
	}
	void get_tag(int x,int addtag,int multitag)
	{
		tree[x].sum=(tree[x].sum*multitag+addtag*tree[x].size)%mod;
		tree[x].v=(tree[x].v*multitag+addtag)%mod;
		tree[x].multitag=(tree[x].multitag*multitag)%mod;
		tree[x].addtag=(tree[x].addtag*multitag+addtag)%mod;
	}
	void push_down(int x)
	{
		if(tree[x].tag)
		{
			for(int i=0;i<=1;i++)
				if(tree[x].son[i]) reverse(tree[x].son[i]);
			tree[x].tag=0;
		}
		for(int i=0;i<=1;i++)
			if(tree[x].son[i]) get_tag(tree[x].son[i],tree[x].addtag,tree[x].multitag);
		tree[x].addtag=0;
		tree[x].multitag=1;
		push_up(x);
	}
	void splay(int x)
	{
		int now=x;
		s.push(now);
		while(!isroot(now))
		{
			s.push(tree[now].fa);
			now=tree[now].fa;
		}
		while(!s.empty())
		{
			push_down(s.top());
			s.pop();
		}
		while(!isroot(x))
		{
			int y=tree[x].fa,z=tree[y].fa;
			int k1=getson(x,y),k2=getson(y,z);
			if(!isroot(y))
			{
				if(k1==k2) rotate(y);
				else rotate(x);
			}
			rotate(x);
		}
	}
	void access(int x)//打通root->x的splay
	{
		int last=0;
		while(x)
		{
			splay(x);
			tree[x].son[1]=last;
			push_up(last=x);
			x=tree[x].fa;
		}
	}
	void makeroot(int x)
	{
		access(x);
		splay(x);
		reverse(x);
	}
	int findroot(int x)
	{
		access(x);
		splay(x);
		while(tree[x].son[0])
		{
			push_down(x);
			x=tree[x].son[0];
		}
		splay(x);
		return x;
	}
	void split(int x,int y)
	{
		makeroot(x);
		access(y);
		splay(y);
	}
	void link(int x,int y)
	{
		makeroot(x);
		// if(x==findroot(y)) return;
		tree[x].fa=y;
	}
	void cut(int x,int y)
	{
		makeroot(x);
		if(findroot(y)!=x||tree[y].fa!=x||tree[y].son[0]) return;
		tree[y].fa=tree[x].son[0]=0;
	}
}lct;

signed main()
{
	// freopen("in.txt","r",stdin);
	int n,q; cin>>n>>q;
	for(int i=1;i<=n;i++) lct.tree[i].v=1;
	for(int i=1;i<n;i++)
	{
		int x,y; scanf("%lld%lld",&x,&y);
		lct.link(x,y);
	}
	while(q--)
	{
		char s[20]; scanf("%s",s+1);
		if(s[1]=='+')
		{
			int x,y,k; scanf("%lld%lld%lld",&x,&y,&k);
			lct.split(x,y);
			lct.get_tag(y,k,1);
		}
		else if(s[1]=='*')
		{
			int x,y,k; scanf("%lld%lld%lld",&x,&y,&k);
			lct.split(x,y);
			lct.get_tag(y,0,k);
		}
		else if(s[1]=='-')
		{
			int x,y; scanf("%lld%lld",&x,&y);
			lct.cut(x,y);
			scanf("%lld%lld",&x,&y);
			lct.link(x,y);
		}
		else
		{
			int x,y; scanf("%lld%lld",&x,&y);
			lct.split(x,y);
			printf("%lld\n",lct.tree[y].sum);
		}
	}
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值