洛谷P1501 [国家集训队]Tree II

题目描述

一棵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的余数。

输入输出格式

输入格式:

第一行两个整数n,q

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

接下来q行,每行描述一个操作

输出格式:

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

输入输出样例

输入样例#1: 
3 2
1 2
2 3
* 1 3 4
/ 1 1
输出样例#1: 
4

说明

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

LCT+线段树的懒惰标记

LCT 打上板子就可以了。

pushdown时加上线段树的加法、乘法的标记。

根据运算优先级,乘法是要先算的,所以先放,放的时候子树的sumsum ,乘法标记,加法标记,儿子的valval 统统都要乘一遍。

放加法标记的时候,想到线段树的区间大小是稳定的,而Splay并不是,所以还要维护sizesize ,于是子树的sumsum 要加上子树的sizesize 再乘上标记,而儿子的valval 和加法标记直接加上该标记的值

注意:和值等要开 unsigned int (毒瘤。。。)

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#define MAXN 200010
#define MOD 51061
using namespace std;
int n,m,w[MAXN];
struct node{
	int son[2];
	int f,flag,s;
	unsigned v,c,d;
}a[MAXN];
inline int read(){
	int date=0,w=1;char c=0;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
	return date*w;
}
inline bool isroot(int rt){
	return a[a[rt].f].son[0]!=rt&&a[a[rt].f].son[1]!=rt;
}
inline void pushup(int rt){
	if(!rt)return;
	a[rt].v=(a[a[rt].son[0]].v%MOD+a[a[rt].son[1]].v%MOD+w[rt]%MOD)%MOD;
	a[rt].s=a[a[rt].son[0]].s+a[a[rt].son[1]].s+1;
}
inline void update1(int rt,unsigned k){
	w[rt]=(w[rt]+k)%MOD;
	a[rt].v=(a[rt].v+k*a[rt].s)%MOD;
	a[rt].c=(a[rt].c+k)%MOD;
}
inline void update2(int rt,unsigned k){
	w[rt]=(w[rt]*k)%MOD;
	a[rt].v=(a[rt].v*k)%MOD;
	a[rt].d=(a[rt].d*k)%MOD;
	a[rt].c=(a[rt].c*k)%MOD;
}
inline void pushdown(int rt){
	if(!rt)return;
	if(a[rt].flag){
		if(a[rt].son[0])a[a[rt].son[0]].flag^=1;
		if(a[rt].son[1])a[a[rt].son[1]].flag^=1;
		swap(a[rt].son[0],a[rt].son[1]);
		a[rt].flag=0;
	}
	if(a[rt].d!=1){
		if(a[rt].son[0])update2(a[rt].son[0],a[rt].d);
		if(a[rt].son[1])update2(a[rt].son[1],a[rt].d);
		a[rt].d=1;
	}
	if(a[rt].c){
		if(a[rt].son[0])update1(a[rt].son[0],a[rt].c);
		if(a[rt].son[1])update1(a[rt].son[1],a[rt].c);
		a[rt].c=0;
	}
}
inline void turn(int rt){
	int x=a[rt].f,y=a[x].f,k=a[x].son[0]==rt?1:0;
	pushdown(x);pushdown(rt);
	if(!isroot(x)){
		if(a[y].son[0]==x)a[y].son[0]=rt;
		else a[y].son[1]=rt;
	}
	a[rt].f=y;a[x].f=rt;a[a[rt].son[k]].f=x;
	a[x].son[k^1]=a[rt].son[k];a[rt].son[k]=x;
	pushup(x);pushup(rt);
}
void splay(int rt){
	int top=0,stack[MAXN];
	stack[++top]=rt;
	for(int i=rt;!isroot(i);i=a[i].f)stack[++top]=a[i].f;
	while(top)pushdown(stack[top--]);
	while(!isroot(rt)){
		int x=a[rt].f,y=a[x].f;
		if(!isroot(x)){
			if((a[x].son[0]==rt)^(a[y].son[0]==x))turn(rt);
			else turn(x);
		}
		turn(rt);
	}
	pushup(rt);
}
void access(int rt){
	for(int i=0;rt;i=rt,rt=a[rt].f){
		splay(rt);
		a[rt].son[1]=i;
		pushup(rt);
	}
}
inline void makeroot(int rt){access(rt);splay(rt);a[rt].flag^=1;}
inline void split(int x,int y){makeroot(x);access(y);splay(y);}
inline void cut(int x,int y){split(x,y);a[y].son[0]=a[x].f=0;}
inline void link(int x,int y){makeroot(x);a[x].f=y;access(x);splay(x);}
int main(){
	char ch[2];
	int x,y,k;
	n=read();m=read();
	for(int i=1;i<=n;i++)w[i]=a[i].v=a[i].d=a[i].s=1;
	for(int i=1;i<n;i++){
		x=read();y=read();
		link(x,y);
	}
	while(m--){
		scanf("%s",ch);x=read();y=read();
		switch(ch[0]){
			case '+':{
				k=read();
				split(x,y);
				update1(y,k);
				break;
			}
			case '-':{
				cut(x,y);
				x=read();y=read();
				link(x,y);
				break;
			}
			case '*':{
				k=read();
				split(x,y);
				update2(y,k);
				break;
			}
			case '/':{
				split(x,y);
				printf("%u\n",a[y].v);
				break;
			}
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值