Codeforces Round #240 (Div. 1) E:Mashmokh's Designed Problem(Splay+欧拉序)

博客详细介绍了如何利用Splay树维护欧拉序来解决Codeforces Round #240 分类1题目中的问题。内容提到欧拉序的三个关键性质,并指出这些性质使得在Splay树上进行操作可以减少时间复杂度,避免额外的lognlogn次操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

题解:
用Splay维护欧拉序。

欧拉序有如下性质:
1.深度随着距离加减一。
2.两点间(任意出入栈序)的深度最小值为lca的深度加一。
3.出入栈之间为整个子树。

有了如上性质,直接维护欧拉序即可。注意三操作可以直接在Splay 上二分从而少一个 logn log ⁡ n

#include <bits/stdc++.h>
using namespace std;

inline int rd() {
    char ch=getchar(); int i=0,f=1;
    while(!isdigit(ch)) {if(ch=='-')f=-1; ch=getchar();}
    while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=getchar();}
    return i*f;
}

const int N=4e5+50;

int n,q,ind;
vector <int> edge[N];
struct node {
    node *fa,*lc,*rc;
    int dep,tag,id,mn,mx;
    node():fa(NULL),lc(NULL),rc(NULL){} 
    inline void upt() {
        mn=(mx=dep);
        if(lc) mn=min(mn,lc->mn), mx=max(mx,lc->mx);
        if(rc) mn=min(mn,rc->mn), mx=max(mx,rc->mx);
    }
    inline void dec(int v) {tag+=v; mn-=v; mx-=v; dep-=v;}
    inline void pushdown() {
        if(!tag) return;
        if(lc) lc->dec(tag);
        if(rc) rc->dec(tag);
        tag=0;
    }
}Pool[N],*pool=Pool,*bg[N],*ed[N],*rt;
inline node* newnode(int dep,int id) {
    ++pool;
    pool->dep=dep; pool->id=id;
    pool->mn=(pool->mx=dep);
    return pool;
}
inline void rotate(node *x) {
    node *y=x->fa, *z=y->fa;
    if(z) (z->lc==y?z->lc:z->rc)=x;
    y->fa=x; x->fa=z;
    if(y->lc==x) {
        node *b=x->rc;
        x->rc=y; y->lc=b;
        if(b) b->fa=y;
    } else {
        node *b=x->lc;
        x->lc=y; y->rc=b;
        if(b) b->fa=y;
    } y->upt(); x->upt();
}
inline bool which(node *x) {return x->fa->lc==x;}
inline void splay(node *x,node *tar=NULL) {
    static node* sta[N]; static int tl;
    sta[tl=1]=x;
    for(node *y=x; y->fa; y=y->fa) sta[++tl]=y->fa;
    for(int i=tl;i;i--) sta[i]->pushdown();
    while(x->fa!=tar) {
        node *y=x->fa;
        if(y->fa!=tar) {
            if(which(x)==which(y)) rotate(y);
            else rotate(x);
        } rotate(x);
    }
    if(!tar) rt=x;
}
inline node* inc(node *&now,node *v) {
    if(!now) {now=v; return v;}
    inc(now->rc,v); now->rc->fa=now; now->upt();
    return v;
}
inline void dfs(int x,int f) {
    bg[x]=newnode((!f)?1:bg[f]->dep+1,x);
    splay(inc(rt,bg[x]));
    for(int e=0;e<edge[x].size();e++) {
        int v=edge[x][e]; if(v==f) continue;
        dfs(v,x);
    }
    ed[x]=newnode((!f)?1:bg[f]->dep+1,x);
    splay(inc(rt,ed[x]));
}
inline node* find(node *k,int v) {
    k->pushdown();
    if(k->lc && k->lc->mn<=v) return find(k->lc,v);
    if(k->dep==v) return k;
    return find(k->rc,v);
}
inline node* find_pre(node *k) {
    splay(k); k=k->lc;
    while(k->rc) k=k->rc;
    return splay(k),k;
}
inline node* find_suf(node *k) {
    splay(k); k=k->rc;
    while(k->lc) k=k->lc;
    return splay(k),k;
}
int main() {
    n=rd(), q=rd();
    for(int i=1;i<=n;i++) {
        int x=rd();
        while(x--) edge[i].push_back(rd());
    }
    dfs(1,0);
    while(q--) {
        int op=rd(),x=rd(); 
        if(op==1) {
            int y=rd(),v=0;
            splay(bg[x]); splay(ed[y],rt); v=bg[x]->dep+ed[y]->dep;
            if(bg[x]->lc && bg[x]->lc==ed[y]) {swap(x,y); splay(bg[x]); splay(ed[y],rt);}
            printf("%d\n",v-2*(ed[y]->lc->mn-1));
        } else if(op==2) {
            int y=rd(), tar;
            splay(ed[x]); tar=ed[x]->dep-y;
            node *now=ed[x]->rc;
            now=find(now,tar); splay(now);
            node *t1=find_pre(bg[x]), *t2=find_suf(ed[x]);
            splay(t1); splay(t2,rt); 
            node *t3=t2->lc; t2->lc=NULL; t3->fa=NULL;
            t2->upt(); t1->upt(); t3->dec(y-1);
            node *t4=find_pre(ed[now->id]);
            splay(t4); splay(ed[now->id],rt);
            rt->rc->lc=t3; t3->fa=rt->rc;
            rt->rc->upt(); rt->upt();
        } else {
            node *now=rt; int bz=1; ++x;
            do {
                now->pushdown();
                if(now->rc && now->rc->mx>=x) now=now->rc;
                else if(now->dep==x) bz=0;
                else now=now->lc; 
            } while(bz);
            printf("%d\n",now->id);
            splay(now);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值