这道题需要记加法和乘法两个标记,因为运算律所以下放时先下放乘法。所以当需要给一个节点改乘法标记时,也要修改加法标记,相当于把括号打开,将加法标记乘上新的乘法标记。需注意模数的平方超过了int类型的范围。
题目保证加完新边仍为一颗树,但貌似没有保证删边合法,所以我还是判断了一下……
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int N=100010;
const ll mod=51061;
struct node{
ll s,x,add,mul;
int fa,l,r,siz;
bool cov;
}tree[N];
int n,m,q[N];
char str1[5];
inline int read(){
int x=0,f=0;
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 f?-x:x;
}
inline void new1(int p){
swap(tree[p].l,tree[p].r);
tree[p].cov=!tree[p].cov;
}
inline void new2(int p,ll c){
tree[p].x=(tree[p].x+c)%mod;
tree[p].s=(tree[p].s+(c*tree[p].siz)%mod)%mod;
tree[p].add=(tree[p].add+c)%mod;
}
inline void new3(int p,ll c){
tree[p].x=(tree[p].x*c)%mod;
tree[p].s=(tree[p].s*c)%mod;
if(tree[p].add)tree[p].add=(tree[p].add*c)%mod;
tree[p].mul=(tree[p].mul*c)%mod;
}
inline void pushdown(int p){
if(tree[p].cov){
new1(tree[p].l);new1(tree[p].r);
tree[p].cov=false;
}if(tree[p].mul!=1){
new3(tree[p].l,tree[p].mul);new3(tree[p].r,tree[p].mul);
tree[p].mul=1;
}if(tree[p].add){
new2(tree[p].l,tree[p].add);new2(tree[p].r,tree[p].add);
tree[p].add=0;
}
}
inline void update(int p){
tree[p].s=((tree[tree[p].l].s+tree[p].x)%mod+tree[tree[p].r].s)%mod;
tree[p].siz=tree[tree[p].l].siz+1+tree[tree[p].r].siz;
}
inline bool nroot(int p){
return tree[tree[p].fa].l==p||tree[tree[p].fa].r==p;
}
inline void left_rotate(int p){
int q=tree[p].fa,r=tree[q].fa;
tree[q].r=tree[p].l;
if(tree[p].l)tree[tree[p].l].fa=q;
if(nroot(q)){
if(tree[r].l==q)tree[r].l=p;
else tree[r].r=p;
}
tree[q].fa=p;tree[p].l=q;tree[p].fa=r;
update(q);update(p);
}
inline void right_rotate(int p){
int q=tree[p].fa,r=tree[q].fa;
tree[q].l=tree[p].r;
if(tree[p].r)tree[tree[p].r].fa=q;
if(nroot(q)){
if(tree[r].l==q)tree[r].l=p;
else tree[r].r=p;
}
tree[q].fa=p;tree[p].r=q;tree[p].fa=r;
update(q);update(p);
}
inline int getlr(int p){
return tree[tree[p].fa].l==p;
}
inline void rotate(int p){
if(getlr(p))right_rotate(p);
else left_rotate(p);
}
inline void splay(int p){
int cnt=0;
q[++cnt]=p;
for(int i=p;nroot(i);i=tree[i].fa)q[++cnt]=tree[i].fa;
while(cnt)pushdown(q[cnt--]);
while(nroot(p)){
int q=tree[p].fa;
if(nroot(q)){
if(getlr(p)==getlr(q))rotate(q);
else rotate(p);
}rotate(p);
}
}
inline void access(int p){
for(int x=0;p;x=p,p=tree[p].fa){splay(p);tree[p].r=x;update(p);}
}
inline void makeroot(int p){
access(p);splay(p);new1(p);
}
inline int findroot(int p){
access(p);splay(p);
while(tree[p].l){pushdown(p);p=tree[p].l;}
splay(p);
return p;
}
inline void split(int x,int y){
makeroot(x);access(y);splay(y);
}
inline void link(int x,int y){
makeroot(x);tree[x].fa=y;
}
inline void cut(int x,int y){
makeroot(x);
if(findroot(y)!=x||tree[y].fa!=x||tree[y].l)return;
tree[y].fa=tree[x].r=0;update(x);
}
int main(){
n=read();m=read();
for(int i=1;i<=n;i++){
tree[i].x=tree[i].s=1;tree[i].siz=1;
tree[i].fa=tree[i].l=tree[i].r=0;
tree[i].cov=false;tree[i].add=0;tree[i].mul=1;
}
for(int x,y,i=1;i<n;i++){x=read();y=read();link(x,y);}
for(int i=1;i<=m;i++){
scanf("%s",str1);
if(str1[0]=='+'){
int x=read(),y=read(),c=read();
split(x,y);new2(y,1ll*c);
}else if(str1[0]=='-'){
int u1=read(),v1=read(),u2=read(),v2=read();
cut(u1,v1);link(u2,v2);
}else if(str1[0]=='*'){
int x=read(),y=read(),c=read();
split(x,y);new3(y,1ll*c);
}else{int x=read(),y=read();split(x,y);printf("%lld\n",tree[y].s);}
}
return 0;
}