题解 P4315 【月下“毛景树”】

· · 题解

总算是A掉这道题了qwq

真是把小蒟蒻我给榨干了qwq

A掉这道题经过了两天多的努力和一些神奇的经历(有兴趣可以自行观看讨论

咳咳,进入正题

这道题其实就是一个裸的边权树剖,不会树剖的可以右转P3384树剖模板

小蒟蒻一开始很快想到思路就来做这道题,但做完了就发现到处都是问题(果然还是我太菜了qwq),其实这道题的细节部分好像也不是很复杂,稍微注意一下就好了qwq

有几点需要注意:

1:树剖维护的是边权,可以把边权转化到点权上。 怎么转化呢?因为每个孩子节点通向父节点的边是唯一的,所以可以将每个边的边权转到边所连的孩子节点上(可在树剖的第一个dfs中完成)

2:修改一条链上的权值时,要注意链两端的点的lca不能够被修改,因为lca所对应的边权不在这一条链上。

3:Change 操作是修改第k条树枝,k为读入的顺序,而树的存边是双向的,所以要将读入的k乘以二在进行后面的操作。

4:下推标记的时候如果有覆盖标记不要忘了清除加的标记

上面几点有的之前的dalao已经说过了,权当是再做一次总结

注意了以上几点,程序自然就不在话下了qwq

感谢@rehtorbegnaro以及其他dalao的帮助和支持

献上233行代码233

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm> 
#define maxn 2000010
#define ll long long
#define re register
#define FOR(i,l,r) for(re ll i=l;i<=r;i++)
using namespace std;

ll tag1[maxn<<2],tag2[maxn<<2],ans[maxn<<2],head[maxn<<1],a[maxn];//tag1为覆盖,tag2为加 
ll top[maxn],son[maxn],depth[maxn],fa[maxn],siz[maxn],id[maxn],dd[maxn];
ll n,m,k,t,num,cnt,x,y,z,w,q;
string s;

struct hz{
    ll next;
    ll to;
    ll dis;
    ll from;
}h[maxn<<1];

inline void in(ll &x){  //快读使我快乐 
    x=0;ll f=1;char c=getchar();
    while(c<'0'||c>'9'){
        if(c==-1) return;
        if(c=='-')f=-1;
        c=getchar();
    }
    while(c<='9'&&c>='0'){
        x=(x<<1)+(x<<3)+(c^'0');
        c=getchar();
    }
    x=x*f;
}

inline void out(ll a){
    if(a<0){
        a*=-1;
        putchar('-');
    } 
    if(a>=10)out(a/10);
    putchar(a%10+'0');
}

inline void add(ll from,ll to,ll dis){ //邻接表存边 
    h[++num].next=head[from];
    h[num].to=to;
    h[num].from=from;
    h[num].dis=dis;
    head[from]=num;
}
//------------------------------以下为线段树部分
inline ll ls(ll k){
    return k<<1;
}

inline ll rs(ll k){
    return k<<1|1;
}

inline void push_up(ll k){
    ans[k]=max(ans[ls(k)],ans[rs(k)]);
}

inline void push_down(ll p){ //下推标记
    if(tag1[p]!=-1){ //下推覆盖标记时要清楚加标记
        tag1[ls(p)]=tag1[rs(p)]=tag1[p];
        ans[ls(p)]=ans[rs(p)]=tag1[p];
        tag2[ls(p)]=tag2[rs(p)]=0;
        tag1[p]=-1;
    }
    tag2[ls(p)]+=tag2[p];
    tag2[rs(p)]+=tag2[p];
    ans[ls(p)]+=tag2[p];
    ans[rs(p)]+=tag2[p];
    tag2[p]=0;
}

void update1(ll nl,ll nr,ll l,ll r,ll p,ll k){ //区间覆盖
    if(nl<=l&&r<=nr){
        ans[p]=k;
        tag1[p]=k;
        tag2[p]=0;
        return;
    }
    ll mid=(l+r)>>1;
    push_down(p);
    if(nl<=mid) update1(nl,nr,l,mid,ls(p),k);
    if(nr>mid) update1(nl,nr,mid+1,r,rs(p),k);
    push_up(p);
}

void update2(ll nl,ll nr,ll l,ll r,ll p,ll k){ //区间加
    if(nl<=l&&r<=nr){
        ans[p]+=k;
        tag2[p]+=k;
        return;
    }
    ll mid=(l+r)>>1;
    push_down(p);
    if(nl<=mid) update2(nl,nr,l,mid,ls(p),k);
    if(nr>mid) update2(nl,nr,mid+1,r,rs(p),k);
    push_up(p);
}

ll query(ll q_x,ll q_y,ll l,ll r,ll p){ //区间求Max
    if(q_x<=l&&r<=q_y)
      return ans[p];
    ll res=0;
    ll mid=(l+r)>>1;
    push_down(p);
    if(q_x<=mid) res=max(res,query(q_x,q_y,l,mid,ls(p)));
    if(q_y>mid) res=max(res,query(q_x,q_y,mid+1,r,rs(p)));
    return res;
}

void build(ll p,ll l,ll r){ //建树
    tag1[p]=-1;
    tag2[p]=0;
    if(l==r){
        ans[p]=dd[l];
        return;
    }
    ll mid=(l+r)>>1;
    build(ls(p),l,mid);
    build(rs(p),mid+1,r);
    push_up(p);
}
//------------------------------以上为线段树部分
void dfs1(ll f,ll ff){
    depth[f]=depth[ff]+1;
    fa[f]=ff;
    siz[f]=1;
    ll maxson=-1;
    for(re ll i=head[f];i!=0;i=h[i].next){
        if(h[i].to==ff)
          continue;
        dfs1(h[i].to,f);
        a[h[i].to]=h[i].dis;  //把边权化为点权
        siz[f]+=siz[h[i].to];
        if(siz[h[i].to]>maxson){
            maxson=siz[h[i].to];
            son[f]=h[i].to;
        }
    }
}

void dfs2(ll x,ll topf){
    top[x]=topf;
    id[x]=++cnt;
    dd[cnt]=a[x];
    if(!son[x])
      return;
    dfs2(son[x],topf);
    for(re ll i=head[x];i!=0;i=h[i].next){
        if(h[i].to==fa[x]||h[i].to==son[x])
          continue;
        dfs2(h[i].to,h[i].to);
    }
}

void updrange1(ll x,ll y,ll k){ //Change和Cover操作
    while(top[x]!=top[y]){
        if(depth[top[x]]<depth[top[y]])
          swap(x,y);
        update1(id[top[x]],id[x],1,n,1,k);
        x=fa[top[x]];
    }
    if(depth[x]>depth[y])
      swap(x,y);
    update1(id[x]+1,id[y],1,n,1,k); //不能更新lca所以是(id[x]+1)
}

void updrange2(ll x,ll y,ll k){  //Add操作
    while(top[x]!=top[y]){
        if(depth[top[x]]<depth[top[y]])
          swap(x,y);
        update2(id[top[x]],id[x],1,n,1,k);
        x=fa[top[x]];
    }
    if(depth[x]>depth[y])
      swap(x,y);
    update2(id[x]+1,id[y],1,n,1,k); //同上
}

ll qrange(ll x,ll y){  //Max操作
    ll anss=0;
    while(top[x]!=top[y]){
        if(depth[top[x]]<depth[top[y]])
          swap(x,y);
        anss=max(anss,query(id[top[x]],id[x],1,n,1));
        x=fa[top[x]];
    }
    if(depth[x]>depth[y])
      swap(x,y);
    return max(anss,query(id[x]+1,id[y],1,n,1));//同理
}

int main(){
    in(n);
    FOR(i,1,n-1){
        in(x),in(y),in(z);
        add(x,y,z);
        add(y,x,z); //双向加边
    }
    dfs1(1,0);
    dfs2(1,1);
    build(1,1,n);
    while(s!="Stop"){
        if(s[1]=='h'){
            in(x),in(k);
            x*=2; //因为边有重复的,所以乘以二保证是第K条边
            x=depth[h[x].from]>depth[h[x].to]?h[x].from:h[x].to;//深度大的点(子节点)保存权值
            update1(id[x],id[x],1,n,1,k); //Change操作
        }
        if(s[1]=='o'){
            in(x),in(y),in(z);
            updrange1(x,y,z); //Cover操作
        }
        if(s[1]=='d'){
            in(x),in(y),in(z);
            updrange2(x,y,z); //Add操作
        }
        if(s[1]=='a'){
            in(x),in(y);
            out(qrange(x,y));  //Max操作
            puts("");
        }
        cin>>s;
    }
    return 0; //功德圆满
}