1. 程式人生 > 實用技巧 >LuoguP4271 [USACO18FEB]New Barns P 樹的直徑+LCT

LuoguP4271 [USACO18FEB]New Barns P 樹的直徑+LCT

結論:如果邊權非負,則距離樹上一個點最遠的點一定是直徑端點之一.

由此可得我們只需維護樹的直徑就行.

這種維護方法的侷限性在於不可以有刪邊操作.

假如合併兩個連通塊 A,B, 直徑端點分別為 $x_{0},x_{1},x_{2},x_{3}$,新樹直徑的端點一定是在 4 個點中的兩個.

那就兩兩取 max,然後求直徑端點距離 x 的距離用 LCT 中的 split 就行了.

code:

#include <cstdio> 
#include <algorithm>     
#define N 100008      
#define ls s[x].ch[0] 
#define rs s[x].ch[1]   
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;  
struct data {     
    int rev,f,ch[2],si; 
}s[N];  
int p[N],ax[N],bx[N],w[N],sta[N];            
int get(int x) { return s[s[x].f].ch[1]==x; }   
int isr(int x) { return s[s[x].f].ch[0]!=x&&s[s[x].f].ch[1]!=x; }
void pushup(int x) { s[x].si=s[ls].si+s[rs].si+1; }
void rotate(int x) { 
    int old=s[x].f,fold=s[old].f,which=get(x);  
    if(!isr(old))  
        s[fold].ch[s[fold].ch[1]==old]=x;   
    s[old].ch[which]=s[x].ch[which^1];  
    if(s[old].ch[which])  
        s[s[old].ch[which]].f=old;  
    s[x].ch[which^1]=old,s[old].f=x,s[x].f=fold;  
    pushup(old),pushup(x);  
}    
void mark(int x) {  
    swap(ls,rs),s[x].rev^=1; 
}  
void pushdown(int x) {  
    if(s[x].rev) { 
        if(ls) mark(ls); 
        if(rs) mark(rs); 
        s[x].rev=0; 
    }
}
void splay(int x) {  
    int v=0,u=x,fa;  
    for(sta[++v]=u;!isr(u);u=s[u].f) sta[++v]=s[u].f;  
    for(;v;--v) pushdown(sta[v]);  
    for(u=s[u].f;(fa=s[x].f)!=u;rotate(x))  
        if(s[fa].f!=u) rotate(get(fa)==get(x)?fa:x); 
} 
void access(int x) {  
    for(int y=0;x;y=x,x=s[x].f) 
        splay(x),rs=y,pushup(x);    
}  
void makert(int x) {  
    access(x),splay(x),mark(x);  
}
void link(int x,int y) {  
    makert(x),s[x].f=y;    
}
void init() {  
    for(int i=0;i<N;++i) {
        p[i]=i;   
        w[i]=0,ax[i]=bx[i]=i;    
    }
}   
int find(int x) {  
    return p[x]==x?x:p[x]=find(p[x]);  
}    
int Dis(int x,int y) {  
    makert(x),access(y),splay(y);  
    return s[y].si-1;     
}           
struct node {  
    int x,y;    
    node(int x=0,int y=0):x(x),y(y){}  
}ma[10];     
void merge(int x,int y) {      
    int oa=find(x),ob=find(y);                  
    ma[1]=node(ax[oa],Dis(ax[oa],x));   
    ma[2]=node(bx[oa],Dis(bx[oa],x));   
    ma[3]=node(ax[ob],Dis(ax[ob],y));  
    ma[4]=node(bx[ob],Dis(bx[ob],y));     
    int a=0,b=0,c=0;  
    for(int i=1;i<=2;++i) 
        for(int j=3;j<=4;++j)    
            if(ma[i].y+ma[j].y+1>c) c=ma[i].y+ma[j].y+1,a=i,b=j;  
    if(c<max(w[oa],w[ob])) {     
        if(w[ob]>w[oa]) p[oa]=ob;     
        else p[ob]=oa;   
    } 
    else {
        p[oa]=ob;    
        w[ob]=c; 
        ax[ob]=ma[a].x;  
        bx[ob]=ma[b].x;           
    }
    link(x,y);    
}
int main() {  
    // setIO("input");    
    init();  
    int q,x,y,z,n=0; 
    char op[10];  
    scanf("%d",&q);   
    for(int i=1;i<=q;++i) {  
        scanf("%s",op);  
        if(op[0]=='B') {  
            scanf("%d",&x);     
            s[++n].si=1; 
            if(x!=-1) merge(n,x);       
        }  
        if(op[0]=='Q') {            
            scanf("%d",&x),y=find(x);  
            int a=ax[y],b=bx[y];   
            printf("%d\n",max(Dis(x,a),Dis(x,b)));      
        }               
    }
    return 0;
}