题目描述
一棵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;
}