题解 luogu P1501[国家集训队]Tree II(Link-Cut-Tree)

Posted k-qiuly

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解 luogu P1501[国家集训队]Tree II(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
",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
",s[y]);
        }
    }return 0;
}

因为51061 * 5106是会越过int界限的,所以我开的longlong(当然也可以开无符号int)

我居然因为没开longlong调了两个多小时.....

以上是关于题解 luogu P1501[国家集训队]Tree II(Link-Cut-Tree)的主要内容,如果未能解决你的问题,请参考以下文章

洛谷 P1501 [国家集训队]Tree II

洛谷P1501 [国家集训队]Tree II

P1501 [国家集训队]Tree II

洛谷P1501 [国家集训队]Tree IILCT

刷题洛谷 P1501 [国家集训队]Tree II

洛谷P1501 [国家集训队]Tree II(LCT)