题意:
给出一个 n n n个结点的树,初始权值为1,处理 q q q个操作,操作只会是以下四种之一
(1) + x y k +\ x\ y\ k + x y k 在 x x x到 y y y的路径上的每个点的权值 + k +k +k
(2) ∗ x y k *\ x\ y\ k ∗ x y k 在 x x x到 y y y的路径上的每个点的权值 ∗ k *k ∗k
(3) − x 1 y 1 x 2 y 2 -\ x_{1}\ y_{1}\ x_{2}\ y_{2} − x1 y1 x2 y2,删去边 ( x 1 , y 1 ) (x_{1},y_{1}) (x1,y1),加入边 ( x 2 , y 2 ) (x_{2},y_{2}) (x2,y2)
(4) / x y /\ x\ y / x y,查询 x x x到 y y y路径上的点的权值和,模51061
方法:
删/连边显然是lct,但权值修改暴力修改超时了一发,需要用懒标记,线段树怎么用在lct就怎么用, s u m sum sum成员维护当前 s p l a y splay splay的总和,所以和线段树一样,需要维护元素个数,线段树可以 r − l + 1 r-l+1 r−l+1得到,这里需要多加一个 s i z e size size来维护,懒标记直接 s p l i t ( x , y ) split(x,y) split(x,y)后放在 y y y上即可
懒标记优先级高的先处理,注意 p u s h d o w n pushdown pushdown之后清空懒标记
对于询问,我们不需要再次下放懒标记了,因为 s p l i t split split的时候先取出了首尾为 x x x和 y y y的 s p l a y splay splay,然后再一次 s p l a y ( y ) splay(y) splay(y), s p l a y splay splay前会下放懒标记,所以 s p l i t split split完直接查询 t r e e [ y ] . s u m tree[y].sum tree[y].sum即可。
另外,对于题目保证合法的 l i n k / c u t link/cut link/cut操作,我们不要再在判断时调用 f i n d r o o t findroot findroot了,在这里常数过大会超时。
#include<bits/stdc++.h>
#define ll long long
#define int long long
using namespace std;
const int mod=51061;
struct LCT
{
struct node
{
ll v,sum,multitag,addtag;
int tag,son[2],fa,size;
};
stack<int>s;
vector<node>tree;
LCT():tree(100005,{0,0,1,0,0,{0,0},0}){}
bool isroot(int x)
{
return tree[tree[x].fa].son[0]!=x&&tree[tree[x].fa].son[1]!=x;
}
int getson(int x,int y)
{
return x==tree[y].son[1];
}
void push_up(int x)
{
tree[x].size=1;
tree[x].sum=tree[x].v;
for(int i=0;i<=1;i++)
{
tree[x].sum=(tree[x].sum+tree[tree[x].son[i]].sum)%mod;
tree[x].size+=tree[tree[x].son[i]].size;
}
}
void rotate(int x)
{
int y=tree[x].fa,z=tree[y].fa;
int k1=getson(x,y),k2=getson(y,z);
tree[x].fa=z;
if(!isroot(y)) tree[z].son[k2]=x;
tree[y].son[k1]=tree[x].son[!k1];
if(tree[x].son[!k1]) tree[tree[x].son[!k1]].fa=y;
tree[x].son[!k1]=y;
tree[y].fa=x;
push_up(y); push_up(x);
}
void reverse(int x)
{
swap(tree[x].son[0],tree[x].son[1]);
tree[x].tag^=1;
}
void get_tag(int x,int addtag,int multitag)
{
tree[x].sum=(tree[x].sum*multitag+addtag*tree[x].size)%mod;
tree[x].v=(tree[x].v*multitag+addtag)%mod;
tree[x].multitag=(tree[x].multitag*multitag)%mod;
tree[x].addtag=(tree[x].addtag*multitag+addtag)%mod;
}
void push_down(int x)
{
if(tree[x].tag)
{
for(int i=0;i<=1;i++)
if(tree[x].son[i]) reverse(tree[x].son[i]);
tree[x].tag=0;
}
for(int i=0;i<=1;i++)
if(tree[x].son[i]) get_tag(tree[x].son[i],tree[x].addtag,tree[x].multitag);
tree[x].addtag=0;
tree[x].multitag=1;
push_up(x);
}
void splay(int x)
{
int now=x;
s.push(now);
while(!isroot(now))
{
s.push(tree[now].fa);
now=tree[now].fa;
}
while(!s.empty())
{
push_down(s.top());
s.pop();
}
while(!isroot(x))
{
int y=tree[x].fa,z=tree[y].fa;
int k1=getson(x,y),k2=getson(y,z);
if(!isroot(y))
{
if(k1==k2) rotate(y);
else rotate(x);
}
rotate(x);
}
}
void access(int x)//打通root->x的splay
{
int last=0;
while(x)
{
splay(x);
tree[x].son[1]=last;
push_up(last=x);
x=tree[x].fa;
}
}
void makeroot(int x)
{
access(x);
splay(x);
reverse(x);
}
int findroot(int x)
{
access(x);
splay(x);
while(tree[x].son[0])
{
push_down(x);
x=tree[x].son[0];
}
splay(x);
return x;
}
void split(int x,int y)
{
makeroot(x);
access(y);
splay(y);
}
void link(int x,int y)
{
makeroot(x);
// if(x==findroot(y)) return;
tree[x].fa=y;
}
void cut(int x,int y)
{
makeroot(x);
if(findroot(y)!=x||tree[y].fa!=x||tree[y].son[0]) return;
tree[y].fa=tree[x].son[0]=0;
}
}lct;
signed main()
{
// freopen("in.txt","r",stdin);
int n,q; cin>>n>>q;
for(int i=1;i<=n;i++) lct.tree[i].v=1;
for(int i=1;i<n;i++)
{
int x,y; scanf("%lld%lld",&x,&y);
lct.link(x,y);
}
while(q--)
{
char s[20]; scanf("%s",s+1);
if(s[1]=='+')
{
int x,y,k; scanf("%lld%lld%lld",&x,&y,&k);
lct.split(x,y);
lct.get_tag(y,k,1);
}
else if(s[1]=='*')
{
int x,y,k; scanf("%lld%lld%lld",&x,&y,&k);
lct.split(x,y);
lct.get_tag(y,0,k);
}
else if(s[1]=='-')
{
int x,y; scanf("%lld%lld",&x,&y);
lct.cut(x,y);
scanf("%lld%lld",&x,&y);
lct.link(x,y);
}
else
{
int x,y; scanf("%lld%lld",&x,&y);
lct.split(x,y);
printf("%lld\n",lct.tree[y].sum);
}
}
return 0;
}