[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 }
View Code

 

posted @ 2022-10-02 20:49  PYWBKTDA  阅读(63)  评论(0编辑  收藏  举报