【BZOJ2631】tree

【BZOJ2631】tree

Description

  一棵n个点的树,每个点的初始权值为1。对于这棵树有q个操作,每个操作为以下四种操作之一:
  + u v c:将u到v的路径上的点的权值都加上自然数c;
  - u1 v1 u2 v2:将树中原有的边(u1,v1)删除,加入一条新边(u2,v2),保证操作完之后仍然是一棵树;
  * u v c:将u到v的路径上的点的权值都乘上自然数c;
  / u v:询问u到v的路径上的点的权值和,求出答案对于51061的余数。

Input

  第一行两个整数n,q
  接下来n-1行每行两个正整数u,v,描述这棵树
  接下来q行,每行描述一个操作                                                                                                                                                                                                                                                

Output

  对于每个/对应的答案输出一行                                                                                                                                                                                                                                                

Sample Input

3 2
1 2
2 3
* 1 3 4
/ 1 1                                                                                                                                                                                                                                                

Sample Output

4                                                                                                                                                                                                                                                

Hint

数据规模和约定
10%的数据保证,1<=n,q<=2000
另外15%的数据保证,1<=n,q<=5*10^4,没有-操作,并且初始树为一条链
另外35%的数据保证,1<=n,q<=5*10^4,没有-操作
100%的数据保证,1<=n,q<=10^5,0<=c<=10^4                                                                                                                                                                                                                                                

Solution

动态树(题解完结)
。。。。。。
好吧,这确实是一颗明显的动态树,但是题解肯定不是这么写的。
对于+和*两种操作,我们需要的是打标记,-操作就是标准的Link和Cut了,以及最后的询问将u设为根,access(v)即可。
这些都不是重点。我们发现,Splay的节点上同时拥有加和乘两种标记,在处理时先后顺序会造成一定的麻烦。这里蒟蒻我介绍一种从其他大佬哪里学来的方式,能够降低思考难度。
inline void Cal(int v,int mul,int add){if(v==0)return ;
	tree[v].val=(tree[v].val*mul+add)%mod;
	tree[v].sum=(tree[v].sum*mul+tree[v].size*add)%mod;
	tree[v].time=tree[v].time*mul%mod;
	tree[v].add=(tree[v].add*mul+add)%mod;
	return ;
}

可以看到,我们将两个标记同时处理,即不论是*操作还是+操作,我们都维护两个标记。这样就能够避免诸如标记遗漏或者是顺序颠倒的问题了。
注意一下运算的优先级的问题。

CODE

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int mod=51061;
inline int read(){
	char c;int rec=0;
	while((c=getchar())<'0'||c>'9');
	while(c>='0'&&c<='9')rec=rec*10+c-'0',c=getchar();
	return rec;
}
int n,m;
struct LCT_Tree{
	long long F,s[2],val,size;
	long long add,time,sum,rev;
	inline void NewNode(int fa,int x){
		F=fa;val=x;s[0]=s[1]=0;add=0;time=1;size=1;sum=val;return ;
	}
}tree[100005];
inline bool Isroot(int v){return tree[tree[v].F].s[0]!=v&&tree[tree[v].F].s[1]!=v;}
inline void Pushup(int v){
	tree[v].sum=tree[tree[v].s[0]].sum+tree[v].val+tree[tree[v].s[1]].sum;
	tree[v].sum%=mod;
	tree[v].size=tree[tree[v].s[0]].size+1+tree[tree[v].s[1]].size;
	return ;
}
inline void Rotate(int v){
	int p=tree[v].F,g=tree[p].F;
	int t1=(v==tree[p].s[1]),t2=(p==tree[g].s[1]),S=tree[v].s[!t1];
	if(!Isroot(p))tree[g].s[t2]=v;tree[v].F=g;
	tree[p].s[t1]=S;tree[S].F=p;
	tree[v].s[!t1]=p;tree[p].F=v;
	Pushup(p);return ;
}
inline void Cal(int v,int mul,int add){if(v==0)return ;
	tree[v].val=(tree[v].val*mul+add)%mod;
	tree[v].sum=(tree[v].sum*mul+tree[v].size*add)%mod;
	tree[v].time=tree[v].time*mul%mod;
	tree[v].add=(tree[v].add*mul+add)%mod;
	return ;
}
inline void Rev(int v){if(v==0)return ;
    tree[v].rev^=1;swap(tree[v].s[0],tree[v].s[1]);return ;
}
inline void Pushdown(int v){
	if(tree[v].rev){Rev(tree[v].s[0]);Rev(tree[v].s[1]);tree[v].rev=0;}
	Cal(tree[v].s[0],tree[v].time,tree[v].add);
	Cal(tree[v].s[1],tree[v].time,tree[v].add);
	tree[v].time=1;tree[v].add=0;
	return ;
}
inline void Lazy(int v){if(!Isroot(v))Lazy(tree[v].F);Pushdown(v);return ;}
inline void Splay(int v){
	Lazy(v);
	while(!Isroot(v)){
		int p=tree[v].F,g=tree[p].F;
		if(!Isroot(p))(v==tree[p].s[1])^(p==tree[g].s[1])?Rotate(v):Rotate(p);
		Rotate(v);
	}Pushup(v);return ;
}
inline void Access(int v){
	for(int temp=0;v;temp=v,v=tree[v].F)
	{Splay(v);tree[v].s[1]=temp;Pushup(v);}
	return ;
}
inline void Make_Root(int v){Access(v);Splay(v);Rev(v);return ;}
inline void Link(int v1,int v2){Make_Root(v1);tree[v1].F=v2;return ;}
inline void Cut(int v1,int v2){Make_Root(v1);Access(v2);Splay(v2);tree[v2].s[0]=tree[v1].F=0;Pushup(v1);Pushup(v2);return ;}
inline void Plus(int v1,int v2,int x){Make_Root(v1);Access(v2);Splay(v2);Cal(v2,1,x);return ;}
inline void Times(int v1,int v2,int x){Make_Root(v1);Access(v2);Splay(v2);Cal(v2,x,0);return ;}
inline void Ask(int v1,int v2){Make_Root(v1);Access(v2);Splay(v2);cout<<tree[v2].sum%mod<<'\n';return ;}
int main(){
	n=read();m=read();
	int x,y,p,q;char c;
	for(int i=1;i<=n;i++)tree[i].NewNode(0,1);
	for(int i=1;i<n;i++)Link(read(),read());
	for(int i=1;i<=m;i++){
		while((c=getchar())!='+'&&c!='-'&&c!='*'&&c!='/');
		if(c=='+'){
			x=read();y=read();p=read();
			Plus(x,y,p);
		}
		if(c=='-'){
			x=read();y=read();p=read();q=read();
			Cut(x,y);Link(p,q);
		}
		if(c=='*'){
			x=read();y=read();p=read();
			Times(x,y,p);
		}
		if(c=='/'){
			x=read();y=read();Ask(x,y);
		}
	}
	return 0;
}
爆int这种事情总是会发生的,还是long long更好。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值