[loj2398]自然公园
维护一个连通块$S$,初始$S=\{0\}$,考虑拓展$x\not\in S$
- 若$x$与$S$中某点相邻,则需找出$S$中所有与$x$相邻的点,并将$x$加入$S$
- 若$x$不与$S$中某点相邻,则需找出$x$到$0$的某条简单路径上的某点$y$,并拓展$y$
问题1:找出$S$中所有与$x$相邻的点
对于$S$中的点dfs序,二分找到最短的前缀$T$满足$ask(0,x,T\cup\{x\})$
该前缀的最后一个点即与$x$相邻,并将其删除后对$S$的每个连通块重复此过程
问题2:找出$x$到$0$的某条简单路径上的某点$y$
对于$S\cup\{x\}$的补集,二分找到最短的前缀$T$满足$ask(0,x,\complement_{V}T)=0$
该前缀的最后一个点即可作为$y$(取$\complement_{V}(T-\{y\})$的中$x$到$0$的简单路径即可)
另外,由于$x,y$间没有偏序关系,因此需标记$x$防止互相查找
关于询问次数,分为以下几类:
- 检验$x$是否与$S$中某点相邻,是/否均对应于某次拓展,至多$2(n-1)$次
- 找出$S$中所有与$x$相邻的点,这可以看作一棵七叉树,其中至多$m$个非叶节点
每个非叶节点上有$\lceil\log_{2}n\rceil+1$次询问;叶节点至多$6m+1$个,每个有$1$次询问
- 找出$x$到$0$的某条简单路径上的某点$y$,至多调用$n-1$次,每次$\lceil\log_{2}n\rceil$次询问
综上,至多$43388$次询问,可以通过
1 #include<bits/stdc++.h> 2 #include "park.h" 3 using namespace std; 4 typedef vector<int> vi; 5 const int N=1405; 6 int n,a[N],vis[N],dfn[N],Vis[N]; 7 vi S,e[N]; 8 int ask(int x,int y){ 9 if (x>y)swap(x,y); 10 return Ask(x,y,a); 11 } 12 void answer(int x,int y){ 13 if (x>y)swap(x,y); 14 Answer(x,y); 15 e[x].push_back(y); 16 e[y].push_back(x); 17 } 18 void dfs(int k){ 19 dfn[++dfn[0]]=k,vis[k]=-1; 20 for(int i:e[k]) 21 if (vis[i]>0)dfs(i); 22 } 23 void solve1(int k,vi v){ 24 memset(a,0,sizeof(a)); 25 a[k]=1; 26 for(int i:v)a[i]=1; 27 if (!ask(v[0],k))return; 28 memset(vis,0,sizeof(vis)); 29 for(int i:v)vis[i]=1; 30 dfn[0]=0,dfs(v[0]); 31 int l=1,r=dfn[0]; 32 while (l<r){ 33 int mid=(l+r>>1); 34 memset(a,0,sizeof(a)); 35 a[k]=1; 36 for(int i=1;i<=mid;i++)a[dfn[i]]=1; 37 if (ask(v[0],k))r=mid; 38 else l=mid+1; 39 } 40 answer(dfn[l],k); 41 memset(vis,0,sizeof(vis)); 42 for(int i:v) 43 if (i!=dfn[l])vis[i]=1; 44 int m=0;vi v0[7]; 45 for(int i:v) 46 if (vis[i]>0){ 47 dfn[0]=0,dfs(i); 48 for(int j=1;j<=dfn[0];j++)v0[m].push_back(dfn[j]); 49 m++; 50 } 51 for(int i=0;i<m;i++)solve1(k,v0[i]); 52 } 53 int solve2(int k){ 54 dfn[0]=0; 55 for(int i=0;i<n;i++) 56 if (!Vis[i])dfn[++dfn[0]]=i; 57 int l=1,r=dfn[0]; 58 while (l<r){ 59 int mid=(l+r>>1); 60 memset(a,0,sizeof(a)); 61 for(int i=0;i<n;i++)a[i]=1; 62 for(int i=1;i<=mid;i++)a[dfn[i]]=0; 63 if (!ask(0,k))r=mid; 64 else l=mid+1; 65 } 66 return dfn[l]; 67 } 68 void add(int k){ 69 Vis[k]=1; 70 while (1){ 71 memcpy(a,Vis,sizeof(a)); 72 if (ask(0,k))break; 73 add(solve2(k)); 74 } 75 solve1(k,S),S.push_back(k); 76 } 77 void Detect(int T,int _n){ 78 n=_n; 79 S=vector<int>{0},Vis[0]=1; 80 for(int i=1;i<n;i++) 81 if (!Vis[i])add(i); 82 }