題解 洛谷P1501/BZOJ2631【[國家集訓隊]Tree II】
阿新 • • 發佈:2019-01-13
Link-Cut-Tree 的懶標記下傳正確食用方法。
我們來逐步分析每一個操作。
1:+ u v c
:將u到v的路徑上的點的權值都加上自然數c;
- 解決方法:
很顯然,我們可以 split(u,v) 來提取
u,v
這一段區間,提取完了將 Splay(v),然後直接在v
上打加法標記add即可。程式碼:
inline void pushadd(ll x,ll val){//打標記 s[x]+=sz[x]*val,v[x]+=val,add[x]+=val; s[x]%=MOD,v[x]%=MOD,add[x]%=MOD; } inline void split(ll x,ll y){//LCT基本操作split,不再贅述 makeroot(x);Access(y);Splay(y); } //(main函式中): if(op[0]=='+'){ scanf("%lld%lld%lld",&x,&y,&v);//輸入資訊 split(x,y);pushadd(y,v);//提取鏈條&打標記 }
2:- u1 v1 u2 v2
:將樹中原有的邊(u1,v1)刪除,加入一條新邊(u2,v2),保證操作完之後仍然是一棵樹;
- 解決方法:
刪除邊即cut操作,加邊即link操作。
程式碼:
inline void link(ll x,ll y){ makeroot(x);if(findroot(x)!=y)f[x]=y; } inline void cut(ll x,ll y){ makeroot(x);split(x,y); if(findroot(y)==x&&f[x]==y&&!ch[x][1]) f[x]=ch[y][0]=0;return; }//LCT基本操作link&cut,不再贅述 //(main函式中): if(op[0]=='-'){ scanf("%lld%lld",&x,&y);cut(x,y);//刪邊 scanf("%lld%lld",&x,&y);link(x,y);//加邊 }
3:* u v c
:將u到v的路徑上的點的權值都乘上自然數c;
- 解決方法:
很顯然,我們可以split(u,v)來提取
u,v
這一段區間,提取完了將Splay(v),然後直接在v
上打乘法標記mul即可。(跟第一個操作基本同理)程式碼:
inline void pushmul(ll x,ll val){//打標記 s[x]*=val,v[x]*=val,mul[x]*=val,add[x]*=val; s[x]%=MOD,v[x]%=MOD,mul[x]%=MOD,add[x]%=MOD; } //(main函式中): if(op[0]=='*'){ scanf("%lld%lld%lld",&x,&y,&v); split(x,y);pushmul(y,v); }
4:/ u v
:詢問u到v的路徑上的點的權值和,求出答案對於51061
的餘數。
- 解決方法:
我們可以像第1、3操作那樣,先扯出這條鏈來(split(u,v)),因為split操作時最後Splay過了,根節點是v。而Splay的時候又將整棵Splay上的節點資訊都跟新好了(懶標記都下傳了),所以這棵Splay的根節點的點權即為這課Splay的點權和。而這課Splay又代表著
u,v
這一段區間,所以最後只需輸出s[v]即可。程式碼:
//(main函式中):
if(op[0]=='/'){
scanf("%lld%lld",&x,&y);
split(x,y);printf("%lld\n",s[y]);
}
Code:
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define RI register ll
#define A printf("A")
#define C printf(" ")
#define MOD 51061
using namespace std;
const ll N=1e5+2;
template<typename _Tp> inline void IN(_Tp& dig){
char c;bool flag=0;dig=0;
while(c=getchar(),!isdigit(c))if(c=='-')flag=1;
while(isdigit(c))dig=dig*10+c-'0',c=getchar();
if(flag)dig=-dig;
}ll f[N],s[N],v[N],sz[N],rev[N],mul[N],add[N],hep[N],ch[N][2];
inline ll get(ll x){return ch[f[x]][0]==x||ch[f[x]][1]==x;}
inline ll chk(ll x){return ch[f[x]][1]==x;}
inline void pushfilp(ll x){
swap(ch[x][0],ch[x][1]);rev[x]^=1;
}
inline void pushup(ll x){
s[x]=(s[ch[x][0]]+s[ch[x][1]]+v[x])%MOD;
sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1;
}
inline void pushmul(ll x,ll val){
s[x]*=val,v[x]*=val,mul[x]*=val,add[x]*=val;
s[x]%=MOD,v[x]%=MOD,mul[x]%=MOD,add[x]%=MOD;
}
inline void pushadd(ll x,ll val){
s[x]+=sz[x]*val,v[x]+=val,add[x]+=val;
s[x]%=MOD,v[x]%=MOD,add[x]%=MOD;
}
inline void pushdown(ll x){
if(mul[x]!=1)pushmul(ch[x][0],mul[x]),pushmul(ch[x][1],mul[x]);
if(add[x])pushadd(ch[x][0],add[x]),pushadd(ch[x][1],add[x]);
if(rev[x]){
if(ch[x][0])pushfilp(ch[x][0]);
if(ch[x][1])pushfilp(ch[x][1]);
}rev[x]=0,add[x]=0,mul[x]=1;return;
}
inline void rotate(ll x){
ll y=f[x],z=f[y],k=chk(x),v=ch[x][!k];
if(get(y))ch[z][chk(y)]=x;ch[x][!k]=y,ch[y][k]=v;
if(v)f[v]=y;f[y]=x,f[x]=z;pushup(y),pushup(x);
}
inline void Splay(ll x){
ll y=x,top=0;hep[++top]=y;
while(get(y))hep[++top]=y=f[y];
while(top)pushdown(hep[top--]);
while(get(x)){
y=f[x],top=f[y];
if(get(y))rotate((ch[y][0]==x)^(ch[top][0]==y)?y:x);
rotate(x);
}pushup(x);return;
}
inline void Access(ll x){
for(register ll y=0;x;x=f[y=x])
Splay(x),ch[x][1]=y,pushup(x);
}
inline ll findroot(ll x){
Access(x);Splay(x);
while(ch[x][0])pushdown(x),x=ch[x][0];
return x;
}
inline void makeroot(ll x){
Access(x);Splay(x);pushfilp(x);
}
inline void split(ll x,ll y){
makeroot(x);Access(y);Splay(y);
}
inline void link(ll x,ll y){
makeroot(x);if(findroot(x)!=y)f[x]=y;
}
inline void cut(ll x,ll y){
makeroot(x);split(x,y);
if(findroot(y)==x&&f[x]==y&&!ch[x][1])
f[x]=ch[y][0]=0;return;
}char op[2];
int main(){
ll n,m,x,y;scanf("%lld%lld",&n,&m);
for(register int i=1;i<=n;++i)
mul[i]=sz[i]=v[i]=1;ll v;
for(register int i=1;i<n;++i)
scanf("%lld%lld",&x,&y),link(x,y);
for(register int i=1;i<=m;++i){
scanf("%s",op);
if(op[0]=='+'){
scanf("%lld%lld%lld",&x,&y,&v);
split(x,y);pushadd(y,v);
}else if(op[0]=='-'){
scanf("%lld%lld",&x,&y);cut(x,y);
scanf("%lld%lld",&x,&y);link(x,y);
}else if(op[0]=='*'){
scanf("%lld%lld%lld",&x,&y,&v);
split(x,y);pushmul(y,v);
}else if(op[0]=='/'){
scanf("%lld%lld",&x,&y);
split(x,y);printf("%lld\n",s[y]);
}
}return 0;
}