【LOJ】#2341. 「WC2018」即时战略-LCT/动态点分治

传送门:loj2341


题解

链要求的复杂度 n + log ⁡ n n+\log n n+logn
树要求的复杂度 n log ⁡ n n\log n nlogn

显然要单独考虑链的情况:
1 1 1开始维护端点 l , r l,r l,r表示已知的区间 [ l , r ] [l,r] [l,r],每次新加入点和 l l l查询一次判断该点在区间左侧/右侧,把端点不断移过去即可。

时间复杂度期望下 n + ln ⁡ n n+\ln n n+lnn

树的情况:

比较显然的暴力把1当做根,每次新加入点和 1 1 1查询得到的点必然是 1 1 1的儿子,如果已知且不是新加入点就不断向下走。复杂度 O ( n 2 ) O(n^2) O(n2)

考虑动态点分治使得树高为 log ⁡ n \log n logn,运用替罪羊树暴力重构的思想维护。

同样也可以简单地用LCT维护这个过程,每次在重链上二分,如果不在重链上就直接向下跳虚儿子。
比较好写但常数大。

然而两个方法我的代码都被卡了TAT


代码

两份都被卡了只有90+

LCT

#include<bits/stdc++.h>
#include"rts.h"
using namespace std;
const int N=3e5+10;

int n,id[N];
bool col[N];

inline void sp()
{
    int i,j,x,v,l=1,r=1;
    for(int i=1;i<n;++i) if(!col[id[i]]){
        x=id[i];v=explore(l,x);
        if(!col[v]){
            for(;;v=explore(l,x)){
                col[(l=v)]=true;
                if(v==x) break;
            }
        }else{
            for(;;){
                v=explore(r,x);
				col[(r=v)]=true;
				if(v==x) break;
            }
        }
    }
}

namespace LCT{

#define lc(x) t[x].ch[0]
#define rc(x) t[x].ch[1]
#define F(x) t[x].fa
#define notrt(x) ((F(x))&&((lc(F(x))==x)||(rc(F(x))==x)))

struct node{int ch[2],fa,l,r;}t[N];

inline void pu(int x)
{
	t[x].l=t[x].r=x;
	if(lc(x)) t[x].l=t[lc(x)].l;
	if(rc(x)) t[x].r=t[rc(x)].r;
}

inline void rot(int x){
    int y=F(x),z=F(y),dr=(rc(y)==x);
    t[y].ch[dr]=t[x].ch[dr^1];
    if(t[y].ch[dr]) F(t[y].ch[dr])=y;
    F(x)=z;if(notrt(y)) t[z].ch[rc(z)==y]=x;
    F(y)=x;t[x].ch[dr^1]=y;pu(y);pu(x);
}

inline void splay(int x)
{
    int y,z;
    for(;notrt(x);rot(x)){
        y=F(x);z=F(y);
        if(notrt(y)) 
         ((rc(y)==x)^(rc(z)==y))?rot(x):rot(y);
    }
    pu(x);
}

inline void setrs(int x,int rs){splay(x);rc(x)=rs;pu(x);}

inline void aces(int x)
{for(setrs(x,0);F(x);x=F(x)) setrs(F(x),x);}

}

using namespace LCT;
void play(int nn,int T,int dataType)
{
	srand(1732);
    col[1]=true;n=nn;
    for(int i=1;i<n;++i) id[i]=i+1;
    random_shuffle(id+1,id+n);
    if(dataType==3) sp();
    else{
        int i,x,v,nw;splay(1);
        for(i=1;i<n;++i) if(!col[id[i]]){
            x=id[i];
			for(splay((nw=1));nw^x;){
                v=explore(nw,x);
                if(v==t[rc(nw)].l) nw=rc(nw);
                else if(v==t[lc(nw)].r) nw=lc(nw);
                else if(col[v]){splay(v);nw=v;}
                else{col[v]=true;F(v)=nw;pu((nw=v));}
            }
           aces(x);
        }
    }
}

动态点分治

#include<bits/stdc++.h>
#include"rts.h"
using namespace std;
const int N=3e5+10;
const double alpha=0.74;
int n,id[N];
bool col[N];

inline void sp()
{
    int i,j,x,v,l=1,r=1;
    for(int i=1;i<n;++i) if(!col[id[i]]){
        x=id[i];v=explore(l,x);
        if(!col[v]){
            for(;;v=explore(l,x)){
                col[(l=v)]=true;
                if(v==x) break;
            }
        }else{
            for(;;){
                v=explore(r,x);
				col[(r=v)]=true;
				if(v==x) break;
            }
        }
    }
}

int rt=1,root,siz[N],sz[N],mx[N],dr[N],F[N],bel[N];
int head[N],to[N<<1],nxt[N<<1],tot,BN,S,MN;
bool cn[N];
map<int,int>g[N];

inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}

//_____DFZ
void fdrt(int x,int fr)
{
    int i,j;sz[x]=1;mx[x]=0;
    for(i=head[x];i;i=nxt[i]){
        j=to[i];if(j==fr || (!cn[j])) continue;
        fdrt(j,x);sz[x]+=sz[j];mx[x]=max(mx[x],sz[j]);
    }
    mx[x]=max(mx[x],S-sz[x]);
    if(mx[x]<MN) MN=mx[x],root=x;
}

void divide(int x)
{
    int i,j,sum=S;siz[x]=S;cn[x]=false;
    for(i=head[x];i;i=nxt[i]){
        j=to[i];if(!cn[j]) continue;
        S=sz[j]<sz[x]?sz[j]:(sum-sz[x]);MN=N;
        fdrt(j,0);F[root]=x;bel[root]=j;
        g[x][j]=root;divide(root);
    }
}

//OPERATION

void clr(int x)
{
	if(!x) return;cn[x]=true;
	for(map<int,int>::iterator it=g[x].begin();it!=g[x].end();++it)
	  clr(it->second);
    g[x].clear();
}

void ins(int x)
{
	int nw,v;
	for(nw=rt;;){
		v=explore(nw,x);
		if(!col[v]){
			col[v]=true;g[nw][v]=bel[v]=v;
			F[v]=nw;lk(nw,v);lk(v,nw);siz[v]=1;
			break;
		}nw=g[nw][v];
	}
	for(BN=0;v^rt;v=F[v]){
		siz[F[v]]++;
		if(siz[F[v]]*alpha<=siz[v]) BN=F[v];
    }
    
    if(BN){
        clr(BN);S=siz[BN];MN=N;fdrt(BN,0);
        if(BN==rt) rt=root;else g[F[BN]][bel[BN]]=root;
        F[root]=F[BN];bel[root]=bel[BN];
        
        divide(root);
    }
}

void play(int nn,int T,int dataType)
{
	srand(173222);
    col[1]=true;n=nn;
    for(int i=1;i<n;++i) id[i]=i+1;
    random_shuffle(id+1,id+n);
    if(dataType==3) sp();
    else{
    	for(int i=1;i<n;++i) 
		  for(;!col[id[i]];ins(id[i]));
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值