[WC2018] 即时战略

题目

终于会写交互了

粗略观察一下,发现这道题树的部分查询次数要求是\(O(n\log n)\)级别,链部分要求是\(O(n)\)级别而且要求这个常数比较小

先来考虑链怎么搞,我们可以随机一个序列,之后按照顺序explore这些点,如果一个点已经被探明了,我们就跳过它;我们维护当前已经扩展出来的这条链的左右端点,每次遇到一个未探明点时,就用这左右两个端点分别explore一下,看看这个点在哪个端点的下面,之后一直搞下去,直到这个点被探明为止,之后再将这个点设为新的端点。

不难发现这样每个点最多会被explore两次,这询问次数好像有点超的样子;但是我们随机了序列,这相当于对链的两边做随机分治,于是询问次数是期望\(n+\log n\)的。

再来考虑树的情况,一个直观的想法是分治,设当前的根为\(x\),我们把\(x\)子树内部的点跟\(x\)explore一遍,之后按照得到的结果把他们分到不同的子树中去;这样的询问次数是\(\sum_{i=1}^ndep_i\),又能过完全二叉树的subtask了。

这个询问次数太垃圾了,我们需要做的是将每个点的询问次数优化到\(\log n\)

假如我们现在有一棵点分树,如果我们要探明一个点\(x\),我们就explore一下当前分治重心与\(x\);我们把得到的结果一路暴跳,这样就能知道下一层的分治重心是谁,之后继续到下一层的分治重心去询问,直到\(x\)被探明,之后再把\(x\)加入点分树。

这样探明\(x\)的询问次数是点分树的树高次,时间复杂度是点分树树高的平方次,于是只要我们能保证点分树\(\log\)的树高,就能解决这个问题了。于是是我们只需要一个替罪羊重构就好了。

这样询问次数是\(O(n\log n)\)的,时间复杂度是\(O(n\log^2 n)\)

代码

#include<bits/stdc++.h>
#include "rts.h"
#define re register
#define max(a,b) ((a)>(b)?(a):(b))
const int maxn=3e5+5;
int c[maxn],p[maxn];
std::vector<int> d[maxn];
void dfs(int x,int fa,int n) {
	for(re int i=2;i<=n;i++) {
		if(c[i]) continue;
		int v=explore(x,i);
		if(c[v]) continue;
		c[v]=1;dfs(v,x,n);
	}
}
void divide(int x) {
	int rt[2];rt[0]=rt[1]=0;
	for(re int i=0;i<d[x].size();++i) {
		if(x==d[x][i]) continue;
		int t=explore(x,d[x][i]);
		d[t].push_back(d[x][i]);
		if(rt[0]==t||rt[1]==t) continue;
		if(!rt[0]) rt[0]=t;
		else if(!rt[1]) rt[1]=t;
	}
	if(rt[0]) divide(rt[0]);
	if(rt[1]) divide(rt[1]);
}
const double alpha=0.75;
struct E{int v,nxt;}e[maxn<<1];
int head[maxn],Mnow,sum[maxn],vis[maxn],nrt,S,rt,mx[maxn],fa[maxn],num,tim,del[maxn];
inline void add(int x,int y) {
	e[++num].v=y;e[num].nxt=head[x];head[x]=num;
}
void getrt(int x,int Fa) {
	sum[x]=1;mx[x]=0;
	for(re int i=head[x];i;i=e[i].nxt) {
		if(vis[e[i].v]==tim||e[i].v==Fa)continue;
		getrt(e[i].v,x);sum[x]+=sum[e[i].v];
		mx[x]=max(sum[e[i].v],mx[x]);
	}
	mx[x]=max(mx[x],S-sum[x]);
	if(mx[x]<Mnow) Mnow=mx[x],nrt=x;
}
void clr(int x,int Fa) {
	del[x]=0;
	for(re int i=head[x];i;i=e[i].nxt) {
		if(e[i].v==Fa||vis[e[i].v]==tim) continue;
		clr(e[i].v,x);
	}
}
void Dfs(int x) {
	vis[x]=tim;
	for(re int i=head[x];i;i=e[i].nxt) {
		if(vis[e[i].v]==tim) continue;
		Mnow=maxn,S=sum[e[i].v];getrt(e[i].v,0);
		fa[nrt]=x;sum[nrt]=sum[e[i].v];Dfs(nrt);
	}
}
inline void rebuild(int x) {
	++tim;for(re int i=fa[x];i;i=fa[i]) vis[i]=tim;
	clr(x,0);Mnow=maxn,S=sum[x],getrt(x,0);
	if(rt==x) rt=nrt;
	fa[nrt]=fa[x];sum[nrt]=sum[x];Dfs(nrt);
}
inline void pushup(int x) {
	if(!fa[x]) {
		if(del[x]) rebuild(x);
		return;
	}
	++sum[fa[x]];if(sum[fa[x]]*alpha<sum[x]) del[fa[x]]=1;
	pushup(fa[x]);if(del[x]) rebuild(x);
}
inline void ins(int x) {
	int u=rt;
	while(!c[x]) {
		int v=explore(u,x);
		if(c[v]) {
			while(fa[v]!=u) v=fa[v];
			u=v;continue;
		}
		add(u,v),add(v,u);fa[v]=u;
		sum[v]=1;pushup(v);
		u=v;c[v]=1;
	}
}
void play(int n, int T, int dataType) {
	if(n<=100&&dataType==1) {
		dfs(1,0,n);return;
	}
	if(dataType==3) {
		for(re int i=1;i<n;++i)p[i]=i+1;
		srand(time(0));
		std::random_shuffle(p+1,p+n);
		int rt[2];rt[0]=1,rt[1]=1;
		int t=p[2],u;
		while(rt[1]!=t) {
			u=explore(rt[1],t);
			c[u]=1;rt[1]=u;
		}
		for(re int i=3;i<n;i++) {
			int x=p[i];if(c[x]) continue;
			u=explore(rt[0],x);
			if(c[u]) std::swap(rt[0],rt[1]),u=explore(rt[0],x);
			c[u]=1,rt[0]=u;
			while(rt[0]!=x) {
				u=explore(rt[0],x);
				c[u]=1,rt[0]=u;
			}
		}
	}
	if(dataType==2) {
		for(re int i=2;i<=n;i++) d[1].push_back(i);
		divide(1);
		return;
	}
	rt=1;for(re int i=1;i<n;i++)p[i]=i+1;
	srand(time(0));std::random_shuffle(p+1,p+n);
	for(re int i=1;i<n;i++) if(!c[p[i]]) ins(p[i]);
}
posted @ 2020-01-12 15:37  asuldb  阅读(157)  评论(0编辑  收藏  举报