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;
}
}
}

浙公网安备 33010602011771号