[WC2018]即时战略(LCT,splay上二分)

[UOJ题面]http://uoj.ac/problem/349

一道非常好的与数据结构有关的交互题。

首先先看部分分做法,
一上来我们肯定得钦定一个 \(explore\) 的顺序,直接随机就好。

\(n\) 很小的时候就是直接从 1 号点一路 \(explore\) 过去就好了,这样次数是 \(O(n^2)\) 的。

由于完全二叉树树高是 log 的,所以它实际也能过第二个包。

然后来看一下链的情况,稍加思考我们可以得到这么一个做法:就是维护当前已经 \(explore\) 的点的连续区间的左右端点,这样就只需往两边扩展了
这部分是 \(O(n)\) 的。能通过第三个包。

我们把这个想法移到树上,于是就拿一个LCT来维护已经 \(explore\) 出来的点。

那么我们的问题就是怎么快速找到一个新点他在哪,然后我们写一个 \(Splay\) 上二分。
因为询问得到的是 \(now\) 的邻点,而当前这颗 \(Splay\)\(now\) 的邻点只有前驱和后继,那么只要分三种情况讨论一下下一步去哪里就行:
(1)前驱(2)后继(3)它属于另一个 \(Splay\),直接去它那个根就行。
至于找前驱跟后继的话,可以拿 \(Splay\) 顺便维护出来。(可以参考Code)

一些细节:
为了保证复杂度,每次寻点结束时记得 \(Access\)
找根的时候不能 \(Splay\) 否则过不去 \(\text{Extra Test}\)

最后把算法三、四合起来就行。

#include "rts.h"
#include "algorithm"
using namespace std;

const int N=300005;
int vis[N];
int p[N];

#define lc(x) (ch[x][0])
#define rc(x) (ch[x][1])

int ch[N][2],f[N],L[N],R[N];

int g(int x)
{
    return rc(f[x])==x;
}

int nrt(int x)
{
    return lc(f[x])==x || rc(f[x])==x;
}

void up(int x)
{
    L[x]=R[x]=x;
    if(lc(x)) L[x]=L[lc(x)];
    if(rc(x)) R[x]=R[rc(x)];
}

void rot(int x)
{
    int y=f[x],i=g(x);
    if(nrt(y))
        ch[f[y]][g(y)]=x;
    f[x]=f[y];
    ch[y][i]=ch[x][i^1];
    f[ch[x][i^1]]=y;
    ch[x][i^1]=y;
    f[y]=x;
    up(y);
}

void splay(int x)
{
    for(int y=f[x];nrt(x);rot(x),y=f[x])
        if(nrt(y))
            rot(g(x)==g(y)?y:x);
    up(x);
}

void access(int x)
{
    for(int y=0;x;y=x,x=f[x])
    {
        splay(x);
        rc(x)=y;
    }
}

int gr(int x)
{
    for(;nrt(x);x=f[x]); return x;
}

void play(int n, int T, int dataType) {
    vis[1]=1;
    if(dataType==2)
    {
        for(int i=2; i<=n; i++)
        {
            if(vis[i])
                continue;
            int now=1;
            while(now!=i) {
                now=explore(now,i);
                vis[now]=1;
            }
        }
    }
    else if(dataType==3)
    {
        for(int i=1;i<=n;i++)p[i]=i;
        int l,r;
        l=r=1;
        srand(20030118);
        for(int i=1;i<=5*n;i++)
        {
            int x=rand()%n+1;
            int y=rand()%n+1;
            swap(p[x],p[y]);
        }
        for(int i,j=1;j<=n;j++)
        {
            i=p[j];
            if(vis[i])
                continue;
            int now=explore(l,i);
            if(!vis[now])
            {
                vis[now]=1;
                while(now!=i) {
                    now=explore(now,i);
                    vis[now]=1;
                }
                l=i;
            }
            else
            {
                now=explore(r,i);
                vis[now]=1;
                while(now!=i) {
                    now=explore(now,i);
                    vis[now]=1;
                }
                r=i;
            }
        }
    }
    else
    {
        for(int i=1;i<=n;i++)p[i]=i;
        srand(20030118);
        for(int i=1;i<=5*n;i++)
        {
            int x=rand()%n+1;
            int y=rand()%n+1;
            swap(p[x],p[y]);
        }
        for(int i,j=1;j<=n;j++)
        {
            i=p[j];
            if(vis[i])
                continue;
            int now=gr(1);
            while(now!=i)
            {
                while(1)
                {
                    int t=explore(now,i);
                    if(t==R[lc(now)]) now=lc(now);
                    else if(t==L[rc(now)]) now=rc(now);
                    else {
                        if(!vis[t])
                            vis[t]=1,f[t]=now;
                        now=gr(t);
                        break;
                    }
                }
            }
            access(now);
        }
    }
}

猜你喜欢

转载自www.cnblogs.com/bestwyj/p/10902869.html