洛谷P1501 [国家集训队]Tree II 题解
题目链接:P1501 [国家集训队]Tree II
题意:树上区间加&乘&link&cut
显然LCT模板题,因为树链剖分并不能维护动态连边
考虑如何维护区间乘
不知道大家有没有做过洛谷的线段树2,没做过也没关系
对于每个结点,维护一个 $kx+b$ 的懒标记
每次做乘法就整体(包括两个部分)乘上 $c$ ,每次做加法就直接加上 $c$
就可以维护标记了
由于加法的维护需要知道这条链的长,那我们只要维护每个结点的子树大小即可
那么这题就很容易解决了(就是函数有点多)
时间复杂度 $O(n\log n)$
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define gc() getchar()
#define pc(a) putchar(a)
#define N (int)(1e5+5)
const int mod=51061;
template<typename T>void read(T &k)
{
char ch=gc();T x=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
k=x*f;
}
template<typename T>void write(T k)
{
if(k<0){k=-k;pc('-');}
if(k>9)write(k/10);
pc(k%10+'0');
}
int n,Q;
namespace LCT
{
struct node
{
int ch[2],num,fa;
int ans,tag,a,m,sz;
}t[N];
#define isroot(x) ((t[t[x].fa].ch[0]!=x)&&(t[t[x].fa].ch[1]!=x))
void pushr(int x)
{
swap(t[x].ch[0],t[x].ch[1]);
t[x].tag^=1;
}
void pusha(int x,int c)
{
t[x].ans=(t[x].ans+c*t[x].sz%mod)%mod;
t[x].a=(t[x].a+c)%mod;t[x].num=(t[x].num+c)%mod;
}
void pushm(int x,int c)
{
t[x].ans=t[x].ans*c%mod;
t[x].num=t[x].num*c%mod;
t[x].m=t[x].m*c%mod;
t[x].a=t[x].a*c%mod;
}
void push_up(int x)
{
t[x].ans=(t[t[x].ch[0]].ans
+t[t[x].ch[1]].ans+t[x].num)%mod;
t[x].sz=t[t[x].ch[0]].sz+t[t[x].ch[1]].sz+1;
}
void push_down(int x)
{
if(t[x].m!=1)
{
pushm(t[x].ch[0],t[x].m);
pushm(t[x].ch[1],t[x].m);
t[x].m=1;
}
if(t[x].a)
{
pusha(t[x].ch[0],t[x].a);
pusha(t[x].ch[1],t[x].a);
t[x].a=0;
}
if(t[x].tag)
{
if(t[x].ch[0])pushr(t[x].ch[0]);
if(t[x].ch[1])pushr(t[x].ch[1]);
t[x].tag=0;
}
}
void push_all(int x)
{
if(!isroot(x))push_all(t[x].fa);
push_down(x);
}
void rotate(int x)
{
int y=t[x].fa;
int z=t[y].fa;
int k=t[y].ch[1]==x;
if(!isroot(y))t[z].ch[t[z].ch[1]==y]=x;
t[x].fa=z;
t[y].ch[k]=t[x].ch[k^1];
t[t[x].ch[k^1]].fa=y;
t[x].ch[k^1]=y;
t[y].fa=x;
push_up(y);
push_up(x);
}
void splay(int x)
{
push_all(x);
while(!isroot(x))
{
int y=t[x].fa;
int z=t[y].fa;
if(!isroot(y))
(t[z].ch[1]==y)^(t[y].ch[1]==x)?rotate(x):rotate(y);
rotate(x);
}
}
void access(int x)
{
for(int y=0;x;y=x,x=t[x].fa)
splay(x),t[x].ch[1]=y,push_up(x);
}
void make_root(int x)
{
access(x);splay(x);
pushr(x);
}
int find_root(int x)
{
access(x);splay(x);
while(t[x].ch[0])push_down(x),x=t[x].ch[0];
splay(x);
return x;
}
void split(int x,int y)
{
make_root(x);
access(y);splay(y);
}
void link(int x,int y)
{
make_root(x);
if(find_root(y)!=x)t[x].fa=y;
}
void cut(int x,int y)
{
make_root(x);
if(find_root(y)==x&&t[y].fa==x&&!t[y].ch[0])
{
t[x].ch[1]=t[y].fa=0;
push_up(x);
}
}
}
signed main()
{
using namespace LCT;
read(n);read(Q);
for(int i=1; i<=n; i++)
t[i].num=t[i].m=t[i].sz=1;
for(int i=1,u,v; i<n; i++)
{
read(u);read(v);
link(u,v);
}
while(Q--)
{
char op;int x,y,c,d;
scanf("%c",&op);
read(x);read(y);
if(op=='+')
{
read(c);
split(x,y);pusha(y,c);
}
if(op=='-')
{
read(c);read(d);
cut(x,y);link(c,d);
}
if(op=='*')
{
read(c);
split(x,y);pushm(y,c);
}
if(op=='/')
{
split(x,y);
write(t[y].ans);pc('\n');
}
}
return 0;
}