uoj349【WC2018】即时战略

题目链接
WC出了点意外滚粗了,来补补题。
\(O(n^2)\)的时间复杂度,\(O(nlogn)\)的询问次数应该还是比较好想的,每次要打通到x的路径,对当前已知的树不断的找重心并询问在重心的哪颗子树中走过去。注意到有加点的操作,用紫荆花之恋的那种做法可以做到\(O(nlog^2n)\),但我不会写...听wys讲课说可以用LCT,想了一下发现挺简单的就去用LCT写了这题。反正就是把1作为根,每次要打通到x的路径时,就可以从1开始,类似二分的过程在每棵splay中不断询问当前点再往其中一棵子树走过去,若得到了一个不在当前splay中的点就需要跳到下一棵splay,找到了目标点就access一下,时间复杂度和询问次数都是\(O(nlogn)\)的,所以链部分的数据要特判一下。

#include "rts.h"
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define P puts("lala")
#define cp cerr<<"lala"<<endl
#define fi first
#define se second
#define ln putchar('\n')
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;

const int N=300050;
int p[N],fa[N],ch[N][2],L[N],R[N];
bool know[N];

inline bool ge(int x) {return ch[fa[x]][1]==x;}
inline bool isroot(int x) {return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
inline void up(int x)
{
    L[x]=x; R[x]=x;
    if(ch[x][0]) L[x]=L[ch[x][0]];
    if(ch[x][1]) R[x]=R[ch[x][1]];
}
inline void rotate(int x)
{
    int f=fa[x],g=fa[f],wh=ge(x);
    if(!isroot(f)) ch[g][ch[g][1]==f]=x;
    ch[f][wh]=ch[x][wh^1]; fa[ch[f][wh]]=f;
    ch[x][wh^1]=f; fa[f]=x;
    fa[x]=g;
    up(f); up(x);
}
int stk[N],top=0;
void splay(int x)
{
    top=0; stk[++top]=x;
    for(int i=x;!isroot(i);i=fa[i]) stk[++top]=fa[i];
    while(top) pushdown(stk[top--]);
    for(int f;(f=fa[x])&&!isroot(x);rotate(x))
        if(!isroot(f)) rotate(ge(x)==ge(f)?f:x);
}
void access(int x)
{
    for(int t=0;x;t=x,x=fa[x])
        splay(x),ch[x][1]=t,up(x);
}


void play(int n, int T, int opt)
{
    for(int i=1;i<=n;++i) p[i]=i;
    random_shuffle(p+1,p+1+n);
    know[1]=1;
    if(opt==3)
    {
        int l=1,r=1;
        for(int i=1;i<=n;++i) if(!know[p[i]])
        {
            int x=l,y=explore(x,p[i]);
            bool rig=0;
            if(know[y]) x=r,rig=1;
            else know[y]=1,x=y,rig=0,l=x;
            while(x!=p[i]) 
            {
                x=explore(x,p[i]),know[x]=1;
                if(rig) r=x;
                else l=x;
            }
        }
    }
    else
    {
        for(int i=1;i<=n;++i) L[i]=R[i]=i;
        for(int i=1;i<=n;++i) if(!know[p[i]])
        {
            int x=1;
            while(1)
            {
                splay(x);
                while(1)
                {
                    int o=explore(x,p[i]);
                    if(o==R[ch[x][0]]) x=ch[x][0];
                    else if(o==L[ch[x][1]]) x=ch[x][1];
                    else
                    {
                        if(!know[o]) know[o]=1,fa[o]=x;
                        x=o; break;
                    }
                }
                if(x==p[i]) break;
            }
            access(x);
        }
    }
}

转载于:https://www.cnblogs.com/thkkk/p/8440475.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值