题解:
容易注意到和一个点直接相关的只有三个点。
首先我们想把这三个点找出来。我们可以通过暴力 O ( n 2 ) O(n^2) O(n2) 询问来实现。
稍微优美一点,考虑我们已经找出了前 i − 1 i-1 i−1 个点之间的所有连边,由于原图是二分图,所以前 i − 1 i-1 i−1 个点我们可以考虑分成两个集合,同一个集合之间没有连边,然后我们可以通过二分找到 i i i 与哪些点在一起时颜色数会减少,进而找到边。
再优美一点,随机化,暴力 Query 分组,然后把所有能找出来的找出来。
然后,我们要确定相连的点中那个和它是同色。
发现直接做很难,不过我们容易发现,当 Query 它,和它同色以及喜欢它的,得到的结果为 1,其他两种方式得到结果为 2,这样可以确定它喜欢的编号,进而确定除了同色以外的所有边。
另外需要注意 L L i = i L_{L_i}=i LLi=i 的情况,这种时候只找得到一条边。
代码:
#include"chameleon.h"
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
//int Query(const std::vector<int> &p);
//void Answer(int a, int b);
using std::cerr;
using std::cout;
int get(int x,std::vector<int> &p){
int l=0,r=p.size()-1;
while(l<r){
int mid=(l+r)>>1;
std::vector<int> vec;
vec.push_back(x);
for(int re i=l;i<=mid;++i)
vec.push_back(p[i]);
if(Query(vec)!=(int)vec.size())r=mid;
else l=mid+1;
}return l;
}
cs int N=1e3+7;
std::vector<int> G[N];
bool con[N][N],vs[N];
void Solve(int n){
srand(time(0));
std::vector<int> A,B;
for(int i=1;i<=n+n;++i)
B.push_back(i);
std::random_shuffle(B.begin(),B.end());
while(B.size()){
auto vec=B;A.clear(),B.clear();
for(int x:vec){
A.push_back(x);
if(Query(A)!=(int)A.size())
A.pop_back(),B.push_back(x);
}for(int u:B){
vec=A;vec.push_back(u);
do{
vec.pop_back();
int k=get(u,vec),v=vec[k];
G[u].push_back(v);
G[v].push_back(u);
vec.erase(vec.begin()+k);
vec.push_back(u);
}while(Query(vec)!=(int)vec.size());
}
}
for(int re i=1;i<=n+n;++i)
if(G[i].size()==3){
int A=G[i][0],B=G[i][1],C=G[i][2];
if(Query({i,A,B})==1)con[i][C]=con[C][i]=true;
else if(Query({i,A,C})==1)con[i][B]=con[B][i]=true;
else con[i][A]=con[A][i]=true;
}
for(int re i=1;i<=n+n;++i)
if(!vs[i])for(int re v:G[i])
if(!con[v][i])vs[i]=vs[v]=true,Answer(i,v);
}