LCT模板题2 最长链

树是任意两点间仅有一条路径的联通图,树上的一条链定义为两个点之间的路径。在本题中定义一条链的长度为链上所有点的权值和。现有一棵带点权树,要对它进行一些操作,请你在第一次操作前和每一次操作后输出这棵树的最长链。

LCT splay的时候判GF 是!isroot(F) 下次注意

这题非常不错的虚边维护儿子信息的LCT,并且,利用LCT splay 维护的是链的性质,可以动态的利用最大子段和的方式进行合并,主要思想就是维护以x节点为根的子树所表示的链的从头开始的最大子段,从尾向前的最大子段(为了reverse),动态的合并其虚儿子的信息求出答案.
套路:LCT维护的是路径信息,序列上能做的,基本都能搬到LCT上来做,剩下的路径统计可以利用数据结构维护虚边.

代码:

%:pragma GCC optimize(4)
%:pragma GCC optimize("inline")
#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 5;

int min(int x , int y) {
    return (x < y) ? x : y;
}

int max(int x , int y) {
    return (x > y) ? x : y;
}

#define lc ch[x][0]
#define rc ch[x][1]

struct NODE {
    int LMAX , RMAX , S , MAX;
}C , T[N];

multiset <int> Cl[N] , Cm[N];

multiset <int> ::reverse_iterator IT;

bool rev[N];

int n , m , x , y , rt , ch[N][2] , fa[N] , val[N] , u[N] , v[N];

inline NODE operator + (NODE A , NODE B) {
    C.S = A.S + B.S;
    C.LMAX = max(A.S + B.LMAX , A.LMAX);
    C.RMAX = max(B.RMAX , B.S + A.RMAX);
    C.MAX = max(A.MAX , max(B.MAX , A.RMAX + B.LMAX));
    return C;
}

inline int get(int x) {
    if(!Cl[x].size()) return 0;
    return *Cl[x].rbegin();
}

inline int get2(int x) {
    if(Cl[x].size() < 2) return get(x);
    IT = Cl[x].rbegin();
    return*IT+*(++ IT);
}

inline void up(int x) {
    NODE &D = T[x]; 
    D.S = D.MAX = D.LMAX = D.RMAX = 0;
    D.S = val[x];
    D.MAX = D.LMAX = D.RMAX = max(max(val[x] , 0) , get(x) + val[x]);
    D.MAX = max(get2(x) + val[x] , max(D.MAX , (Cm[x].size()) ? (*Cm[x].rbegin()) : 0));
    if(lc) D = T[lc] + D;
    if(rc) D = D + T[rc];
}

inline void pt(int x) {
    rev[x] ^= 1;
    swap(lc , rc); 
    swap(T[x].LMAX , T[x].RMAX);
}

inline void pd(int x) {
    if(rev[x]) {
        if(lc)pt(lc); if(rc)pt(rc);
        rev[x] = 0;
    }
}

inline bool dir(int x) {
    return ch[fa[x]][1] == x;
}

#define isroot(x) (ch[fa[x]][1] != x && ch[fa[x]][0] != x)

inline void dn(int x) {
    if(!isroot(x)) dn(fa[x]);
    pd(x);
}

int Dx , f , Df , GF;

inline void rotate(int x) {
    Dx = dir(x) , f = fa[x] , Df = dir(fa[x]) , GF = fa[f];
    if(!isroot(f)) ch[GF][Df] = x; fa[x] = GF; fa[f] = x; if(ch[x][!Dx]) fa[ch[x][!Dx]] = f;
    ch[f][Dx] = ch[x][!Dx]; ch[x][!Dx] = f; up(f); up(x);
}

inline void splay(int x) {
    dn(x);
    while(!isroot(x)) {
        if(isroot(fa[x])) {
            rotate(x); return;
        }
        if(dir(x) == dir(fa[x])) rotate(fa[x]) , rotate(x);
        else rotate(x) , rotate(x);
    }
}

#define del(K , P) K.erase(K.find(P))

inline void A(int x , int y , int oh) {
    if(oh == 1) {
        Cl[x].insert(T[y].LMAX);
        Cm[x].insert(T[y].MAX);
    }
    else {
        del(Cl[x] , T[y].LMAX);
        del(Cm[x] , T[y].MAX);
    }
}

inline void access(int x) {
    int t = 0;
    for(;x;t = x , x = fa[x]){
        splay(x);
        if(rc)A(x , rc , 1);
        if(t) A(x , t , -1);
        rc = t; up(x);
    }
}

inline void make_root(int x) {
    access(x);splay(x); pt(x); rt = x;
}

inline void link(int x , int y) {
    make_root(x);make_root(y);fa[y] = x;A(x , y , 1); up(x);rt = x;
}

inline void cut(int x , int y) {
    make_root(x); access(y); splay(y); ch[y][0] = fa[x] = 0; up(y);
}

inline int getans(void) {
    access(rt); return T[rt].MAX;
}

char ty[23];

bool is;

void read(int &x) {
    char ch = getchar(); x = 0; is = 0;
    while(!isdigit(ch)) {
        if(ch == '-') is = 1; ch = getchar();
    }
    while(isdigit(ch)) x = x * 10 + ch - '0' , ch = getchar();
    if(is) x = -x;
}

main(void) {
    read(n); read(m);
    register int i;
    for(i = 1;i < n;++ i) read(u[i]) , read(v[i]);
    for(i = 1;i <= n;++ i) read(val[i]) , up(i);
    for(i = 1;i < n;++ i) link(u[i] , v[i]);
    printf("%d\n" , getans());
    for(i = 1;i <= m;++ i) {
        scanf("%s" , ty);
        if(ty[0] == 'M') {
            read(x); read(y);
            make_root(x); val[x] = y;
        }
        else {
            read(x); read(y); cut(x , y);
            read(x); read(y); link(x , y);
        }
        printf("%d\n" , getans());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值