LOJ#6038. 「雅礼集训 2017 Day5」远行(LCT)

题面

传送门

题解

要不是因为数组版的\(LCT\)跑得实在太慢我至于去学指针版的么……而且指针版的完全看不懂啊……

首先有两个结论

1.与一个点距离最大的点为任意一条直径的两个端点之一

2.两棵树之间连一条边新树直径的端点一定是第一棵树直径的两个端点和第二颗树直径的两个端点这四个点之二

然后用并查集维护联通块的直径就行了。注意因为这里强制在线,所以得用\(LCT\)来维护距离

并不建议看代码因为这个代码非常难懂哪怕我加满注释您都不一定看得懂

//minamoto
#include<bits/stdc++.h>
#define R register
#define inline __inline__ __attribute__((always_inline))
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
inline int getop(){R char ch;while((ch=getc())>'9'||ch<'0');return ch-'0';}
char sr[1<<21],z[20];int C=-1,Z=0;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
void print(R int x){
    if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++C]=z[Z],--Z);sr[++C]='\n';
}
const int N=3e5+5;
struct node;typedef node* ptr;
inline void swap(R ptr &x,R ptr &y){R ptr t=x;x=y,y=t;}
inline int max(R int &x,R int &y){return x>y?x:y;}
struct node{
    ptr fa,lc,rc;int s;bool r;
    inline node();
    inline void ppd(){swap(lc,rc),r^=1;}
    inline void pd(){if(r)lc->ppd(),rc->ppd(),r=0;}
    inline ptr upd(){return s=lc->s+rc->s+1,this;}
}e[N];
inline node::node(){fa=lc=rc=e;}
inline bool isrt(R ptr p){return p->fa->lc!=p&&p->fa->rc!=p;}
void rotate(ptr p){
    ptr s=p->fa,t=s->fa;
    if(!isrt(s))(t->lc==s?t->lc:t->rc)=p;
    p->fa=t,s->fa=p;
    if(s->lc==p)s->lc=p->rc,p->rc->fa=s,p->rc=s->upd();
        else s->rc=p->lc,p->lc->fa=s,p->lc=s->upd();
}
void push(ptr p){if(!isrt(p))push(p->fa);p->pd();}
ptr splay(ptr p){
    push(p);
    while(!isrt(p)){
        if(!isrt(p->fa))rotate(p==p->fa->lc^p->fa==p->fa->fa->lc?p:p->fa);
        rotate(p);
    }
    return p->upd();
}
ptr exp(ptr p){
    ptr s=e;
    while(p!=e)splay(p)->rc=s,s=p->upd(),p=p->fa;
    return s;
}
ptr exp(R int i){return exp(e+i);}
struct qwq{
    int s,t,d;
    inline qwq(){}
    inline qwq(R int ss,R int tt,R int dd):s(ss),t(tt),d(dd){}
}p[N];
int ga[N];
int find(int x){return ga[x]==x?x:ga[x]=find(ga[x]);}
void link(int i,int j){
    exp(i)->ppd(),exp(j)->ppd();
    //上面两步已经完成了makeroot操作了 
    //虽然还没有把i和j给splay上去 
    int u=find(i),v=find(j);
    int s1=exp(p[u].s)->s,s2=exp(p[u].t)->s;
    //分别计算直径的两个端点到根节点的距离
    //注意这里的距离 是指它们之间的点数,也包括根节点 
    int s3=exp(p[v].s)->s,s4=exp(p[v].t)->s;
    qwq a=p[u];
    if(s1+s3-1>a.d)a=qwq(p[u].s,p[v].s,s1+s3-1);
    if(s1+s4-1>a.d)a=qwq(p[u].s,p[v].t,s1+s4-1);
    if(s2+s3-1>a.d)a=qwq(p[u].t,p[v].s,s2+s3-1);
    if(s2+s4-1>a.d)a=qwq(p[u].t,p[v].t,s2+s4-1);
    if(a.d>p[v].d)p[v]=a;
    ga[u]=v,splay(e+i)->fa=e+j;
}
int ask(int i){
    exp(i)->ppd();
    int u=find(i),s1=exp(p[u].s)->s,s2=exp(p[u].t)->s;
    return max(s1,s2)-1;
}
int n,ty,q,ans,op,u,v;
int main(){
//  freopen("testdata.in","r",stdin);
    ty=read(),n=read(),q=read();
    fp(i,1,n)ga[i]=i,p[i]=qwq(i,i,0);
    while(q--){
        op=read(),u=read()^(ans*ty);
        if(op==1)v=read()^(ans*ty),link(u,v);
        else print(ans=ask(u));
    }
    return Ot(),0;
}

猜你喜欢

转载自www.cnblogs.com/bztMinamoto/p/10713567.html