P14390 [JOISC 2017] 自然公园 / Natural Park 题解

题目链接:P14390 [JOISC 2017] 自然公园 / Natural Park#368. Natural Park

先考虑链,假设现在的链端点编号是 $ l $ 和 $ r $,可以先问一次仅有 $ l $ 和 $ r $ 是否通行,如果是那么 $ l, r $ 有边,否则二分当前链上面的最大值,设编号为 $ p $,递归为端点 $ l, p $ 和端点 $ p, r $ 的子问题,然后最终段有 $ n-1 $ 个,每个最终段问一次,非最终段有 $ n-2 $ 个,每个非最终段最多问 $ \lceil log_2 n \rceil + 1 $ 次,显然操作次数数是可以的。

树的话情况是类似的,一开始求出 $ 0, n-1 $ 的链,然后每次在非树上选一个点 $ p $,在当前求出的树上面的 dfn 序二分,每次加入剩下的所有点和树上 dfn 序小于等于 mid 的值,最终可以二分出 $ p $ 在树上连接的点 $ q $,然后按照上面链的做法求出 $ p, q $ 之间的链即可,感性理解一下操作次数肯定不超过上面的两倍,算出来也是合理的。

对于图,假设当前联通块对应的点集为 $ S $,二分不在点集内的点集合编号的一个前缀,然后求出存在连边的最小前缀编号 $ p $(这里解释一下,一开始需要固定一个端点 $ o $,集合 $ S $ 因为是联通块选哪个点是没有问题的,对于固定的 $ o $,二分出 $ p $,可以发现 $ o, p $ 一定存在一条链,$ p, {S} $ 一定存在一条链,按照链的方法做即可,任意一个点只会在这个二分的过程中遇到一次(添加到 $ S $ 时),因此是 $ nlogn $ 的),然后 $ p $ 显然和 $ S $ 有一条链(注意这里不是直接连边,因为询问不是直接询问两个联通块是否连通,而是固定的某两个点,例如 0 - 2 - 3 - 1,其中 $ S={0} $,二分到 $ 2 $ 的时候由于其中一个端点是 $ 1 $ 显然是不对的,询问当前二分的值也是不对的,因为函数不单调($ p $ 和 $ p+1 $ 可能不连通))。然后按照链的做法二分出直接连接 $ S $ 的点 $ q $,然后二分点集 $ S $(需要按照 dfn 序二分而不是直接按编号二分)求出和 $ q $ 直接连接的最小编号的点 $ r $,然后删除 $ r $ 并对分出的每一个联通块询问 $ q $ 是否和当前联通块有连边,如果有边重复以上过程找出每条边并报告。

操作次数对于每次找到一个链加入每个点,考虑每个点只会被加入一次(找到一次),因此找到 $ p $ 和找到 $ q $ 操作次数加在一起显然是不超过 $ nlogn $ 的,然后找到所有 $ r $,每次询问的联通块数根据题目给出的连边数小于等于 $ 7 $,其中和 $ q $ 有一条边,所以分出的联通块数不超过 $ 6 $,因此询问是否有连边是 $ 6m $ 次的(找到每条边至多多问 $ 6 $ 次),找到每条真正连接的边的最小点需要二分点集,是 $ mlogn $ 次的。最终不超过 $ nlogn + mlogn + 6m $ 次询问,实现的时候可能还带有 eps 个 $ n $。

代码:

#include<bits/stdc++.h>
#define pii pair<int,int>
#define pb push_back
using namespace std;
void Answer(int A, int B);
int Ask(int A, int B, int Place[]);
vector<int> e[1405];
int flag[1405],n,dfn[1405],tot,vis[1405],rnk[1405],nvis[1405];
struct node{
	int u,v,mx; 
};
void add(int u,int v)
{
//	cout<<u<<' '<<v<<'\n';
	if(u>v)swap(u,v);
	Answer(u,v);
	e[u].pb(v);
	e[v].pb(u);
}
bool query(int u,int v,vector<int> T)
{
	if(u>v)swap(u,v);
	int Place[1400]={0};
	for(auto x:T)
	{
		Place[x]=1;
	}
	if(!Place[u])Place[u]=1;
	if(!Place[v])Place[v]=1;
	return Ask(u,v,Place);
}
void dfs(int u,int fa)
{
	dfn[u]=++tot;
	for(auto v:e[u])
	{
		if(v==fa||dfn[v])continue;
		dfs(v,u);
	}
}
void dfs2(int u,int fa,vector<int> &T)
{
	dfn[u]=++tot;
	T.pb(u);
	for(auto v:e[u])
	{
		if(v==fa||vis[v]||dfn[v])continue;
		dfs2(v,u,T);
	}
}
void dfs3(int u,int fa)
{
	nvis[u]=1;
	for(auto v:e[u])
	{
		if(v==fa||vis[v]||nvis[v])continue;
		dfs3(v,u);
	}
}
pii chain(int x,vector<int> &S)
{
	vector<int> T=S;
	T.pb(x);
	if(query(0,x,T))
	{
		int y=0;
		tot=0;
		for(int i=0;i<n;i++)
		{
			dfn[i]=0;
		}
		dfs(0,-1);
		vector<int> rnk(S.size()+1);
		for(int i=0;i<n;i++)
		{
			if(dfn[i])rnk[dfn[i]]=i;
		}
		int l=1,r=tot,rans=tot;
		while(l<=r)
		{
			int mid=l+r>>1;
			T=vector<int>();
			T.pb(x);
			for(int i=1;i<=mid;i++)
			{
				T.pb(rnk[i]);
			}
			if(query(0,x,T))r=mid-1,rans=mid;
			else l=mid+1;
		}
		y=rnk[rans];
		add(x,y);
		S.pb(x);
		flag[x]=1;
		return {x,y};
	}
	vector<int> notin;
	for(int i=0;i<n;i++)
	{
		if(!flag[i])notin.pb(i);
	}
	int l=0,r=notin.size()-1,qans=0;
	while(l<=r)
	{
		int mid=l+r>>1;
		T=S;
		for(int i=0;i<=mid;i++)
		{
			T.pb(notin[i]);
		}
		if(query(0,x,T))qans=mid,r=mid-1;
		else l=mid+1;
	}
	queue<node> q;
	q.push({x,notin[qans],qans});
	q.push({notin[qans],0,qans});
	pii ret;
	vector<pii> need;
	while(q.size())
	{
		node now=q.front();
		q.pop();
		vector<int> cur;
		if(now.v==0)cur=S;
		cur.pb(now.u);
		if(now.v!=0)cur.pb(now.v);
		if(query(now.u,now.v,cur))
		{
			if(now.v==0)
			{
				int y=0;
				tot=0;
				for(int i=0;i<n;i++)
				{
					dfn[i]=0;
				}
				dfs(0,-1);
				for(int i=0;i<n;i++)
				{
					if(dfn[i])rnk[dfn[i]]=i;
				}
				l=1,r=tot;
				int rans=tot;
				while(l<=r)
				{
					int mid=l+r>>1;
					T=vector<int>();
					T.pb(now.u);
					for(int i=1;i<=mid;i++)
					{
						T.pb(rnk[i]);
					}
					if(query(0,now.u,T))r=mid-1,rans=mid;
					else l=mid+1;
				}
				y=rnk[rans];
				need.pb({now.u,y});
				ret={now.u,y};
				continue;
			}
			else
			{
				need.pb({now.u,now.v});
				continue;
			}
		}
		if(now.v==0)
		{
			l=0,r=now.mx-1,qans=0;
			while(l<=r)
			{
				int mid=l+r>>1;
				cur=S;
				for(int i=0;i<=mid;i++)
				{
					cur.pb(notin[i]);
				}
				if(query(now.u,now.v,cur))r=mid-1,qans=mid;
				else l=mid+1;
			}
			q.push({now.u,notin[qans],qans});
			q.push({notin[qans],now.v,qans});
		}
		else
		{
			l=0,r=now.mx-1,qans=0;
			while(l<=r)
			{
				int mid=l+r>>1;
				cur=vector<int>();
				for(int i=0;i<=mid;i++)
				{
					cur.pb(notin[i]);
				}
				if(query(now.u,now.v,cur))r=mid-1,qans=mid;
				else l=mid+1;
			}
			q.push({now.u,notin[qans],qans});
			q.push({notin[qans],now.v,qans});
		}
	}
	for(auto x:need)
	{
		add(x.first,x.second);
		if(!flag[x.first])
		{
			S.pb(x.first);
			flag[x.first]=1; 
		}
	}
	return ret;
}
void Detect(int _,int _n)
{
	n=_n;
	vector<int> S;
	S.pb(0);
	flag[0]=1;
	while(S.size()<n)
	{
		int x=0;
		for(int i=0;i<n;i++)
		{
			if(!flag[i])x=i;
		}
		pii ret=chain(x,S);
		int q=ret.first,r=ret.second;
		vector<int> tag;
		tag.pb(r);
		vis[r]=1;
		queue<int> qq;
		for(int i=0;i<n;i++)
		{
			nvis[i]=0;
		}
		for(auto v:e[r])
		{
			if(v==q||nvis[v])continue;
			qq.push(v);
			dfs3(v,-1);
		}
		int cnt=6;
		while(qq.size()&&cnt)
		{
			int u=qq.front();
			qq.pop();
			tot=0;
			vector<int> cur;
			for(int i=0;i<n;i++)
			{
				dfn[i]=0;
			}
			dfs2(u,-1,cur);
			for(int i=0;i<n;i++)
			{
				if(dfn[i])rnk[dfn[i]]=i;
			}
			vector<int> T=cur;
			T.pb(q);
			if(query(q,u,T))
			{
				int l=1,r=tot;
				int rans=tot;
				while(l<=r)
				{
					int mid=l+r>>1;
					T=vector<int>();
					T.pb(q);
					for(int i=1;i<=mid;i++)
					{
						T.pb(rnk[i]);
					}
					if(query(u,q,T))r=mid-1,rans=mid;
					else l=mid+1;
				}
				int y=rnk[rans];
				add(q,y);
				vis[y]=1;
				for(int i=0;i<n;i++)
				{
					nvis[i]=0;
				}
				for(auto v:e[y])
				{
					if(v==q||nvis[v]||vis[v])continue;
					qq.push(v);
					dfs3(v,-1);
				}
				tag.pb(y);
				cnt--;
			}
		}
		for(auto x:tag)
		{
			vis[x]=0;
		}
	}
}

posted @ 2026-07-19 10:20  MCYYDS  阅读(5)  评论(0)    收藏  举报