【BZOJ 2631】tree

传送门


Problem

一棵 n n n 个点的树,每个点的初始权值为 1 1 1。对于这棵树有 q q q 个操作,每个操作为以下四种操作之一:

  • + + + u u u v v v c c c:将 u u u v v v 的路径上的点的权值都加上自然数 c c c
  • − - u 1 u_1 u1 v 1 v_1 v1 u 2 u_2 u2 v 2 v_2 v2:将树中原有的边 ( u 1 , v 1 ) (u_1,v_1) (u1,v1) 删除,加入一条新边 ( u 2 , v 2 ) (u_2,v_2) (u2,v2),保证操作完之后仍然是一棵树;
  • ∗ ∗ u u u v v v c c c:将 u u u v v v 的路径上的点的权值都乘上自然数 c c c
  • / / / u u u v v v:询问 u u u v v v 的路径上的点的权值和,求出答案对于 51061 51061 51061 的余数。

Solution

建议先做一下洛谷上的线段树模板 2 2 2

这道题就是把它搬到了树上,然后添加了删边和加边的操作。

就直接用 L C T \mathrm{LCT} LCT 维护就可以了。

注意一下,虽然是对 51061 51061 51061 取模,但是 51060 × 51060 = 2607123600 51060\times 51060=2607123600 51060×51060=2607123600,还是超了 i n t int int 的范围。


Code

#include<stack>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define Mod 51061
using namespace std;
char op[5];
namespace LCT{
	int fa[N],val[N],mul[N],add[N],sum[N],mark[N],Size[N],son[N][2];
	bool Get(int x)  {return son[fa[x]][1]==x;}
	bool isroot(int x)  {return (son[fa[x]][0]!=x)&&(son[fa[x]][1]!=x);}
	void pushup(int x){
		Size[x]=Size[son[x][0]]+Size[son[x][1]]+1;
		sum[x]=(sum[son[x][0]]+sum[son[x][1]]+val[x])%Mod;
	}
	void pushmul(int x,int v){
		sum[x]=1ll*sum[x]*v%Mod;
		add[x]=1ll*add[x]*v%Mod;
		mul[x]=1ll*mul[x]*v%Mod;
		val[x]=1ll*val[x]*v%Mod;
	}
	void pushadd(int x,int v){
		sum[x]=(sum[x]+1ll*v*Size[x]%Mod)%Mod;
		add[x]=(add[x]+v)%Mod,val[x]=(val[x]+v)%Mod;
	}
	void pushflip(int x){
		swap(son[x][0],son[x][1]);
		mark[son[x][0]]^=1,mark[son[x][1]]^=1;
	}
	void pushdown(int x){
		if(mul[x]!=1)  pushmul(son[x][0],mul[x]),pushmul(son[x][1],mul[x]),mul[x]=1;
		if(add[x]!=0)  pushadd(son[x][0],add[x]),pushadd(son[x][1],add[x]),add[x]=0;
		if(mark[x]!=0)  pushflip(x),mark[x]=0;
	}
	void Rotate(int x){
		int y=fa[x],z=fa[y];
		int k=Get(x),l=son[x][k^1];
		son[y][k]=l,fa[l]=(l?y:0);
		if(!isroot(y))  son[z][Get(y)]=x;fa[x]=z;
		son[x][k^1]=y,fa[y]=x;
		pushup(y),pushup(x);
	}
	stack<int>stk;
	void Splay(int x){
		stk.push(x);
		for(int i=x;!isroot(i);i=fa[i])  stk.push(fa[i]);
		while(!stk.empty())  pushdown(stk.top()),stk.pop();
		while(!isroot(x)){
			int y=fa[x];
			if(!isroot(y))  Rotate(Get(x)==Get(y)?y:x);
			Rotate(x);
		}
	}
	void Access(int x){
		for(int i=0;x;x=fa[i=x])
			Splay(x),son[x][1]=i,pushup(x);
	}
	void Makeroot(int x){
		Access(x),Splay(x),mark[x]^=1;
	}
	void split(int x,int y){
		Makeroot(x),Access(y),Splay(y);
	}
	void Link(int x,int y){
		Makeroot(x),fa[x]=y;
	}
	void Cut(int x,int y){
		split(x,y);
		fa[x]=son[y][0]=0;
		pushup(y);
	}
}
using namespace LCT;
int main(){
	int n,m,i,x,y,p,q;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;++i)
		val[i]=Size[i]=mul[i]=1;
	for(i=1;i<n;++i){
		scanf("%d%d",&x,&y);
		Link(x,y);
	}
	for(i=1;i<=m;++i){
		scanf("%s",op);
		if(op[0]=='+'){
			scanf("%d%d%d",&x,&y,&p);
			split(x,y),pushadd(y,p);
		}
		else  if(op[0]=='-'){
			scanf("%d%d",&x,&y),Cut(x,y);
			scanf("%d%d",&p,&q),Link(p,q);
		}
		else  if(op[0]=='*'){
			scanf("%d%d%d",&x,&y,&p);
			split(x,y),pushmul(y,p);
		}
		else{
			scanf("%d%d",&x,&y),split(x,y);
			printf("%d\n",sum[y]);
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 内容概要 《计算机试卷1》是一份综合性的计算机基础和应用测试卷,涵盖了计算机硬件、软件、操作系统、网络、多媒体技术等多个领域的知识。试卷包括单选题和操作应用两大类,单选题部分测试学生对计算机基础知识的掌握,操作应用部分则评估学生对计算机应用软件的实际操作能力。 ### 适用人群 本试卷适用于: - 计算机专业或信息技术相关专业的学生,用于课程学习或考试复习。 - 准备计算机等级考试或职业资格认证的人士,作为实战演练材料。 - 对计算机操作有兴趣的自学者,用于提升个人计算机应用技能。 - 计算机基础教育工作者,作为教学资源或出题参考。 ### 使用场景及目标 1. **学习评估**:作为学校或教育机构对学生计算机基础知识和应用技能的评估工具。 2. **自学测试**:供个人自学者检验自己对计算机知识的掌握程度和操作熟练度。 3. **职业发展**:帮助职场人士通过实际操作练习,提升计算机应用能力,增强工作竞争力。 4. **教学资源**:教师可以用于课堂教学,作为教学内容的补充或学生的课后练习。 5. **竞赛准备**:适合准备计算机相关竞赛的学生,作为强化训练和技能检测的材料。 试卷的目标是通过系统性的题目设计,帮助学生全面复习和巩固计算机基础知识,同时通过实际操作题目,提高学生解决实际问题的能力。通过本试卷的学习与练习,学生将能够更加深入地理解计算机的工作原理,掌握常用软件的使用方法,为未来的学术或职业生涯打下坚实的基础。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值