BZOJ 2631 tree Link Cut 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


Source




LCT的模板题,
练习同时乘法和加法标记的题目可以先去做这题:
一定要注意下放的时候先下放乘法标记啊............
坑了我好久!!

这题听说开long long会被卡掉……
然而我竟然过了。。。看上去我的常数比较小?
不过确实耗时非常非常大,
long long改成unsigned int估计会快很多了的。

测过了。。改成unsigned int只用了12s……比long long快了整整一倍。。。


#include<bits/stdc++.h>
#define ll long long
using namespace std;
int read(){
	int x=0,f=1;char ch=getchar();
	while (ch<'0' || ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int 
	N=100005;
const ll 
	moder=(ll)51061;
int n,q;
struct LCT{
	int pre,son[2];
	bool rev,ifplus,ifmulti;
	ll sum,num,plus,multi,sz;
}tree[N];
bool isroot(int x){
	return tree[tree[x].pre].son[0]!=x &&
			tree[tree[x].pre].son[1]!=x;
}
void up(int x){
	if (x){
		int l=tree[x].son[0],r=tree[x].son[1];
		tree[x].sum=tree[x].num,tree[x].sz=(ll)1;
		if (l)
			tree[x].sum=(tree[x].sum+tree[l].sum)%moder,
			tree[x].sz=(tree[x].sz+tree[l].sz)%moder;
		if (r)
			tree[x].sum=(tree[x].sum+tree[r].sum)%moder,
			tree[x].sz=(tree[x].sz+tree[r].sz)%moder;
	}
}
void calcplus(int x,ll num){
	tree[x].plus=(tree[x].plus+num)%moder;
	tree[x].num=(tree[x].num+num)%moder;
	tree[x].sum=(tree[x].sum+(num*tree[x].sz)%moder)%moder;
	tree[x].ifplus=1;
}
void calcmulti(int x,ll num){
	tree[x].multi=(tree[x].multi*num)%moder;
	tree[x].plus=(tree[x].plus*num)%moder;
	tree[x].num=(tree[x].num*num)%moder;
	tree[x].sum=(tree[x].sum*num)%moder;
	tree[x].ifmulti=1;
}
void down(int x){
	int L=tree[x].son[0],R=tree[x].son[1];
	if (tree[x].rev){
		swap(tree[x].son[0],tree[x].son[1]);
		tree[L].rev^=1,tree[R].rev^=1;
		tree[x].rev=0;
	}
	if (tree[x].ifmulti){
		calcmulti(L,tree[x].multi);
		calcmulti(R,tree[x].multi);
		tree[x].multi=(ll)1;
		tree[x].ifmulti=0;
	}
	if (tree[x].ifplus){
		calcplus(L,tree[x].plus);
		calcplus(R,tree[x].plus);
		tree[x].plus=tree[x].ifplus=0;
	}
}
void Rotate(int x){
	int y=tree[x].pre,z=tree[y].pre,l,r;
	if (tree[y].son[0]==x) l=0; else l=1;
	r=l^1;
	if (!isroot(y))
		if (tree[z].son[0]==y) tree[z].son[0]=x;
			else tree[z].son[1]=x;
	tree[x].pre=z,tree[y].pre=x;
	tree[y].son[l]=tree[x].son[r];
	tree[tree[x].son[r]].pre=y;
	tree[x].son[r]=y;
	up(y);
}
int stk[N];
void splay(int x){
	int top=1;stk[1]=x;
	for (int i=x;!isroot(i);i=tree[i].pre)
		stk[++top]=tree[i].pre;
	while (top) down(stk[top--]);
	while (!isroot(x)){
		int y=tree[x].pre,z=tree[y].pre;
		if (!isroot(y))
			if (tree[z].son[0]==y^tree[y].son[0]==x) Rotate(x);
				else  Rotate(y);
		Rotate(x); 
	}
	up(x);
}
void access(int x){
	for (int t=0;x;t=x,x=tree[x].pre)
		splay(x),tree[x].son[1]=t,up(x);
}
void makeroot(int x){
	access(x),splay(x);
	tree[x].rev^=1;
}
void split(int x,int y){
	makeroot(x);
	access(y),splay(y);
}
void link(int x,int y){
	makeroot(x);
	tree[x].pre=y;
}
void cut(int x,int y){
	split(x,y);
	tree[y].son[0]=tree[x].pre=0;
	up(y);
}
ll querysum(int x,int y){
	split(x,y);
	return tree[y].sum;
}
void updateplus(int x,int y,ll num){
	split(x,y);
	calcplus(y,num);
}
void updatemulti(int x,int y,ll num){
	split(x,y);
	calcmulti(y,num);
}
int main(){
	n=read(),q=read();
	for (int i=1;i<=n;i++)
		tree[i].multi=tree[i].num=tree[i].sum=tree[i].sz=(ll)1;
	for (int i=1;i<n;i++) link(read(),read());
	char opt[2]; int u,v;ll c;
	while (q--){
		scanf("%s",opt);
		if (opt[0]=='-')
			cut(read(),read()),link(read(),read());
		else{
			u=read(),v=read();
			if (opt[0]=='/') printf("%lld\n",querysum(u,v));
				else c=(ll)read();
			if (opt[0]=='+') updateplus(u,v,c);
			if (opt[0]=='*') updatemulti(u,v,c);
		}
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值