【WC2018】即时战略(动态点分治,替罪羊树)
【WC2018】即时战略(动态点分治,替罪羊树)
题面
题解
其实这题我也不知道应该怎么确定他到底用了啥。只是想法很类似就写上了QwQ。
首先链的部分都告诉你要特殊处理那就没有办法只能特殊处理了QWQ。
首先听说有一种均摊\(log\)的\(LCT\)做法。
即每次随便\(explore\)一个点,如果这个点未被访问过,直接加入然后继续。
否则在\(LCT\)重链组成的\(Splay\)上跳。
这样子均摊复杂度是\(O(nlogn)\),均摊的询问次数也是\(O(nlogn)\)。
然而似乎会被卡。
另一种做法是动态维护点分树。
假装我们有一个点分树,这样子每次询问的时候,如果确定了一个已知节点,那么我们从当前节点向着那个节点的点分树方向移动,这样子可以证明移动次数不会超过一个\(log\)。
然而新加入若干点之后点分治的树可能不在平衡,导致复杂度不正确了。
那么类似替罪羊树的思想,如果点分树之间的大小不满足平衡,则拆掉重建。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<map>
#include "rts.h"
using namespace std;
#define MAX 300300
const double alpha=0.75;
int n,p[MAX],tot;
bool vis[MAX];
namespace Chain
{
void Solve()
{
int L=1,R=1;
for(int i=1;i<=tot;++i)
{
if(vis[p[i]])continue;
int u=p[i],x=explore(L,u);
if(vis[x])x=R,R=u;
else L=u,vis[x]=true;
while(u!=x)vis[x=explore(x,u)]=true;
}
}
}
struct Line{int v,next;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
int Fa[MAX],size[MAX];
bool del[MAX];
int book[MAX],tim;
void Clear(int u,int ff)
{
del[u]=false;
for(int i=h[u];i;i=e[i].next)
if(e[i].v!=ff&&book[e[i].v]!=tim)
Clear(e[i].v,u);
}
int Size,mx,rt,root;
void Getroot(int u,int ff)
{
int ret=0;size[u]=1;
for(int i=h[u];i;i=e[i].next)
{
int v=e[i].v;if(v==ff||book[v]==tim)continue;
Getroot(v,u);size[u]+=size[v];ret=max(ret,size[v]);
}
ret=max(ret,Size-size[u]);
if(ret<mx)mx=ret,rt=u;
}
void Divide(int u)
{
book[u]=tim;
for(int i=h[u];i;i=e[i].next)
{
int v=e[i].v;if(book[v]==tim)continue;
Size=mx=size[v];Getroot(v,u);
Fa[rt]=u;size[rt]=size[v];Divide(rt);
}
}
void ReBuild(int u)
{
++tim;for(int i=Fa[u];i;i=Fa[i])book[i]=tim;
Clear(u,0);Size=mx=size[u];Getroot(u,0);
if(u==root)root=rt;
Fa[rt]=Fa[u];size[rt]=size[u];Divide(rt);
}
void Update(int u)
{
if(!Fa[u]){if(del[u])ReBuild(u);return;}
++size[Fa[u]];if(Fa[u]&&alpha*size[Fa[u]]<size[u])del[Fa[u]]=true;
Update(Fa[u]);if(del[u])ReBuild(u);
}
void Find(int x)
{
int u=root;
while(!vis[x])
{
int v=explore(u,x);
if(vis[v]){while(u!=Fa[v])v=Fa[v];u=v;}
else Add(u,v),Add(v,u),Fa[v]=u,size[v]=1,Update(v),vis[v]=true,u=v;
}
}
void play(int _n,int T,int dataType)
{
n=_n;vis[1]=true;root=1;size[1]=1;
for(int i=2;i<=n;++i)p[++tot]=i;
random_shuffle(&p[1],&p[tot+1]);
if(dataType==3){Chain::Solve();return;}
for(int i=1;i<=tot;++i)if(!vis[p[i]])Find(p[i]);
}