【UOJ349】【WC2018】即時戰略 LCT 動態點分治

這是一道交互題

題目大意

  有一棵n 個點的樹。最開始1 號點是白的,其他點是黑的。

  每次你可以執行一個操作:explore(x,y) 。要求x 是一個白點。該函數會返回從xy 的路徑上第二個點的座標並把該點染白。

  要求你把所有點都染成白色。

  設操作次數爲t

  對於30% 的數據:這棵樹是一條鏈(不保證1 在鏈的一端),n=300000,t=O(n+logn)

  對於另外70% 的數據:n=300000,t=O(nlogn)

題解

  數據範圍告訴我們鏈的情況要分開做。

做法1

  有一個簡單的做法:維護當前白色節點的兩段,每次加入一個新的節點,如果這個節點是黑的,就先判斷這個點在一號點的左邊還是右邊,在進行擴展。

  可以證明擴展次數是2lnn

  記E(n) 爲一條長爲n+1 的鏈(有n 個黑色節點),從左邊開始擴展的期望次數(最左端是1 號點)。

(1)E(0)=0(2)E(n)=1+1ni=0n1E(i)(3)n(E(n)1)=(n1)(E(n1)1)+E(n1)(4)nE(n)n=nE(n1)n+1E(n1)+1+E(n1)(5)nE(n)nE(n1)=1(6)E(n)E(n1)=1n(7)E(n)=i=1n1ilnn

  就是枚舉n 是第幾個擴展的,把前面的離散化到1i1

  因爲這題的1 號點不在一端,次數就要乘以2

  期望操作次數是n+2lnn

  但是這樣很大概率拿不了滿分。

  我們加入一個新的節點時,可以默認這個點是在1 號點左邊,擴展第一次時判斷要擴展的點是否是白的。如果是白的就說明新加入的點在1 號點右邊。

  當然,加入新節點的第一次擴展要隨機擴展左邊或右邊。

  這樣期望擴展次數就是lnn 了,期望操作次數就是n+lnn

做法2

  問題轉化爲:每次給你一個新的點,要你找出樹上離這個點最近的節點。

  • 動態點分治:動態維護點分治樹,在點分治樹上面跳。

    時間複雜度:O(nlog2n)

    操作次數:O(nlogn)

  • LCT:每次從根往下在splay上跳,如果調到另一條鏈上就splay一下繼續跳。

    時間複雜度:O(nlogn)

    操作次數:O(nlogn)

  這兩種做法都可以拿到滿分。

  如果你寫的是動態點分治,你可能會被卡常。

  UPD:動態點分治跑的很快,LCT沒寫好會被卡操作常數。

代碼

LCT

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<ctime>
#include<cstdlib>
#include<utility>
#include"rts.h"
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
void open(const char *s)
{
#ifndef ONLINE_JUDGE
    char str[100];
    sprintf(str,"%s.in",s);
    freopen(str,"r",stdin);
    sprintf(str,"%s.out",s);
    freopen(str,"w",stdout);
#endif
}
int n;
int b[300010];
int t;
int a[300010][2];
int f[300010];
int l[300010];
int r[300010];
int root(int x)
{
    return !f[x]||(a[f[x]][0]!=x&&a[f[x]][1]!=x);
}
void mt(int x)
{
    l[x]=r[x]=x;
    if(a[x][0])
        l[x]=l[a[x][0]];
    if(a[x][1])
        r[x]=r[a[x][1]];
}
void rotate(int x)
{
    int p=f[x];
    int q=f[p];
    int ps=(x==a[p][1]);
    int qs=(p==a[q][1]);
    int c=a[x][ps^1];
    if(!root(p))
        a[q][qs]=x;
    a[x][ps^1]=p;
    a[p][ps]=c;
    if(c)
        f[c]=p;
    f[p]=x;
    f[x]=q;
    mt(p);
    mt(x);
}
void splay(int x)
{
    while(!root(x))
    {
        int p=f[x];
        if(!root(p))
        {
            int q=f[p];
            if((p==a[q][1])==(x==a[p][1]))
                rotate(p);
            else
                rotate(x);
        }
        rotate(x);
    }
}
void access(int x)
{
    int y=x,t=0;
    while(x)
    {
        splay(x);
        a[x][1]=t;
        mt(x);
        t=x;
        x=f[x];
    }
    splay(y);
}
void gao(int x)
{
    int now=1,v;
    splay(now);
    while(!b[x])
    {
        v=explore(now,x);
        if(v==r[a[now][0]])
            now=a[now][0];
        else if(v==l[a[now][1]])
            now=a[now][1];
        else if(b[v])
        {
            splay(v);
            now=v;
        }
        else
        {
            b[v]=1;
            f[v]=now;
            now=v;
        }
    }
    access(x);
}
void gao1()
{
    int i;
    for(i=2;i<=n;i++)
        if(!b[i])
            gao(i);
}
void gao2()
{
    int x1=1,x2=1;
    int i;
    b[1]=1;
    for(i=2;i<=n;i++)
    {
        int x=i;
        if(b[x])
            continue;
        int v=explore(x1,x);
        if(!b[v])
        {
            b[v]=1;
            x1=v;
            while(x1!=x)
            {
                v=explore(x1,x);
                b[v]=1;
                x1=v;
            }
        }
        else
        {
            while(x2!=x)
            {
                v=explore(x2,x);
                b[v]=1;
                x2=v;
            }
        }
    }
}
void play(int _n,int _t,int type)
{
    n=_n;
    t=_t;
    if(type==3)
        gao2();
    else
        gao1();
}

動態點分治

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<ctime>
#include<cstdlib>
#include<utility>
#include<vector>
#include"rts.h"
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
void open(const char *s)
{
#ifndef ONLINE_JUDGE
    char str[100];
    sprintf(str,"%s.in",s);
    freopen(str,"r",stdin);
    sprintf(str,"%s.out",s);
    freopen(str,"w",stdout);
#endif
}
vector<int> g[300010];
const double alpha=0.8;
int n;
int b[300010];
int t;
int d[300010];
int f[300010];
int s[300010];
int rebuild;
void gao(int now,int x)
{
    if(now==x)
        return;
    int v=explore(now,x);
    if(b[v])
    {
        while(f[v]!=now)
            v=f[v];
        s[now]-=s[v];
        gao(v,x);
        s[now]+=s[v];
    }
    else
    {
        b[v]=1;
        g[now].push_back(v);
        g[v].push_back(now);
        d[v]=d[now]+1;
        s[v]=1;
        f[v]=now;
        gao(v,x);
        s[now]+=s[v];
    }
    if(s[v]>s[now]*alpha)
        rebuild=now;
}
void dfs(int x,int dep)
{
    b[x]=0;
    for(auto v:g[x])
        if(b[v]&&d[v]>=dep)
            dfs(v,dep);
}
int ss[300010];
void dfs1(int x,int fa)
{
    ss[x]=1;
    for(auto v:g[x])
        if(v!=fa&&!b[v])
        {
            dfs1(v,x);
            ss[x]+=ss[v];
        }
}
int rt,rtsz,num;
void dfs2(int x,int fa)
{
    int mx=0;
    for(auto v:g[x])
        if(!b[v]&&v!=fa)
        {
            dfs2(v,x);
            mx=max(mx,ss[v]);
        }
    mx=max(mx,num-ss[x]);
    if(mx<rtsz)
    {
        rtsz=mx;
        rt=x;
    }
}
int build(int x,int dep,int fa,int mi)
{
    dfs1(x,0);
    num=ss[x];
    rtsz=0x7fffffff;
    dfs2(x,0);
    x=rt;
    b[x]=1;
    d[x]=dep;
    f[x]=fa;
    s[x]=1;
    for(auto v:g[x])
        if(!b[v])
        {
            int rr=build(v,dep+1,x,mi);
            s[x]+=s[rr];
        }
    return x;
}
int cc[300010];
void gao1()
{
    int i;
    for(i=1;i<=n;i++)
        cc[i]=i;
    srand(time(0));
    random_shuffle(cc+1,cc+n+1);
    b[1]=1;
    d[1]=0;
    s[1]=1;
    f[1]=0;
    int r=1;
    for(i=1;i<=n;i++)
        if(!b[cc[i]])
//      if(!b[i])
        {
            gao(r,cc[i]);
//          gao(r,i);
            if(rebuild)
            {
                dfs(rebuild,d[rebuild]);
                int rr=build(rebuild,d[rebuild],f[rebuild],d[rebuild]);
                if(rebuild==r)
                    r=rr;
                rebuild=0;
            }
        }
}
void gao3()
{
    int x1=1,x2=1;
    int i;
    b[1]=1;
    for(i=1;i<=n;i++)
        cc[i]=i;
    srand(time(0));
    random_shuffle(cc+1,cc+n+1);
    for(i=1;i<=n;i++)
    {
        int x=cc[i];
        if(b[x])
            continue;
        int v=explore(x1,x);
        if(!b[v])
        {
            b[v]=1;
            x1=v;
            while(x1!=x)
            {
                v=explore(x1,x);
                b[v]=1;
                x1=v;
            }
        }
        else
        {
            while(x2!=x)
            {
                v=explore(x2,x);
                b[v]=1;
                x2=v;
            }
        }
    }
}
void gao2()
{
    int i,v;
    for(i=2;i<=n;i++)
    {
        int x=1;
        while((v=explore(x,i))!=i)
            x=v;
    }
}
void play(int _n,int _t,int type)
{
    n=_n;
    t=_t;
    if(type==3)
        gao3();
    else if(type==2)
        gao2();
    else
        gao1();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章