「UOJ349 WC2018 即时战略」 - LCT

UOJ349 WC2018 即时战略

tags:LCT,交互

题意

有一棵树,你不知道它长啥样,一开始只有 1 是已知的

你每次可以传入 u 和 v,其中 u 要已知,你会得到 u 到 v 的路径上的第二个点,这样 u 到 v 的路径上的第二个点也会变为已知的,让你求出这棵树

有两种情况

  1. 一条链:要求在 \(O(n+\log n)\) 步求出整棵树
  2. 一棵树:要求在 \(O(n\log n)\) 步求出整棵树

题解

首先 random_shuffle 一遍

对于一条链,维护已知点的左右端点,然后随机一个端点暴力探索,如果询问得到的点 x 已被探索过,说明在另一个端点,不停扩展即可

对于一棵树,考虑将他树链剖分,可以在树链上二分转折点,然后往下走

直接用 LCT 维护就好了,LCT 的话每次把加入的新点 access 一下就可以保证复杂度了,我也不是很明白,感觉就很对

#include"rts.h"
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
const int N=300005;
int n,id[N],vis[N],L[N],R[N],fa[N],ch[N][2];
void upd(int u){L[u]=ch[u][0]?L[ch[u][0]]:u,R[u]=ch[u][1]?R[ch[u][1]]:u;}
int get(int u){return ch[fa[u]][1]==u;}
int isroot(int u){return ch[fa[u]][0]!=u&&ch[fa[u]][1]!=u;}
void rotate(int u){
    int p=fa[u],gp=fa[p],x=get(u);
    if(!isroot(p))ch[gp][get(p)]=u;fa[u]=gp;
    ch[p][x]=ch[u][x^1],fa[ch[u][x^1]]=p;
    ch[u][x^1]=p,fa[p]=u;
    upd(p),upd(u);
}
void splay(int u){for(;!isroot(u);rotate(u))if(!isroot(fa[u]))rotate(u==fa[u]?fa[u]:u);}
void access(int u){
    for(int i=0;u;i=u,u=fa[u])
        splay(u),ch[u][1]=i,upd(u);
}
int findroot(int u){while(!isroot(u))u=fa[u];return u;}
void play(int n,int T,int op){
    srand(19260817);
    ::n=n;rep(i,1,n)id[i]=i;L[0]=0x3f3f3f3f,R[0]=-0x3f3f3f3f;
    std::random_shuffle(id+2,id+1+n);
    vis[1]=1;
    if(op==3){
        int a[2]={1,1};
        rep(i,2,n){
            int u=id[i];if(vis[u])continue;
            int x=rand()&1,v=explore(a[x],u);
            if(vis[v])x^=1,v=explore(a[x],u);
            vis[v]=1;while(v!=u)vis[v=explore(v,u)]=1;
            a[x]=u;
        }
        return;
    }
    rep(i,1,n)L[i]=R[i]=i;
    rep(i,2,n){
        if(vis[id[i]])continue;
        int u=id[i],lst=findroot(1);
        while(lst!=u){
            int v=explore(lst,u);
            if(!vis[v])vis[v]=1,fa[v]=lst,lst=v;else
            if(v==R[ch[lst][0]])lst=ch[lst][0];else
            if(v==L[ch[lst][1]])lst=ch[lst][1];else
                lst=findroot(v);
        }
        access(lst);
    }
}

转载于:https://www.cnblogs.com/xay5421/p/UOJ349.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值