Solution
对于任意两个点(x,y),如果它们同时参加会议,得到的颜色只有(1)种,仅有(3)种情况:
(1.)(x,y)颜色相同
(2.)(x)喜欢(y),(y)不喜欢(x)
(3.)(y)喜欢(x),(x)不喜欢(y)
在这样的情况下我们对(x,y)连边,显然,每个点的度数都是(1)或(3)
如果点(x)的度数是(1),那么这个点(y)一定是与(x)颜色相同的点,直接得到答案
否则,考虑依次将(x)与它所连的三个点中任选(2)个进行询问,如果找到的是喜欢(x)的点与和(x)颜色相同的点,那么颜色数是(1),否则颜色数是(2),据此,我们就能推断出剩下的一个点就是(x)喜欢的点
据此,我们就能确定所有(2,3)类型的边,那么剩下的就是我们需要的(1)号类型的边。
因此,如果我们找到了所有的边,就能用约(6n)次询问得出答案。
考虑如何快速找边:
首先,我们考虑在原序列中找到一个极大独立集:这显然可以通过从左至右扫一遍,依次加入当前的独立集(S)进行询问,如果答案是(|S|+1),那么(S)与当前点之间没有边,可已加入。
找到独立集后,对于独立集外的点,我们在独立集中二分找到它们之间的边。
现在,我们就剩下剩余部分内部的边了,递归处理即可。
因为每个点度数(le 3),所以独立集大小(ge)总点数的(frac 14),所以每次将点集的规模缩小到原来的(frac 34),因此总共找独立集的循环数计算一下极限情况也不超过(10n),而找到每一条边都需要(log(n))次查询,一共也大约只需要(3nlog(n))次查询。
因此复杂度是正确的,可以通过此题。
Code
#include "chameleon.h"
#include <bits/stdc++.h>
using namespace std;
const int N=1010;
vector<int> p[N];
int vis[N],lov[N],whlov[N];
inline int ask(int a,int b,int c){
vector<int> p;p.push_back(a);p.push_back(b);p.push_back(c);
return Query(p);
}
inline int ask(vector<int> a,int b){
a.push_back(b);
return Query(a)==a.size();
}
inline int findans(int x,vector<int> v){
if(v.size()==1){
p[x].push_back(v[0]),p[v[0]].push_back(x);
return v[0];
}
int mid=v.size()>>1;
vector<int> L,R;
for(int i=0;i<v.size();++i){
if(i<mid) L.push_back(v[i]);
else R.push_back(v[i]);
}
if(ask(L,x)) return findans(x,R);
else return findans(x,L);
}
void Solve(int n){
vector<int> ve;
for(int i=1;i<=2*n;++i) ve.push_back(i);
while(ve.size()){
vector<int> duli;
vector<int> oth;
for(int i=0;i<ve.size();++i){
if(!duli.size()||ask(duli,ve[i])) duli.push_back(ve[i]);
else oth.push_back(ve[i]);
}
for(int i=0;i<oth.size();++i){
vector<int> rec=duli;
do{
int p=findans(oth[i],rec);
vector<int> ne;
for(int j=0;j<rec.size();++j) if(rec[j]!=p) ne.push_back(rec[j]);
rec=ne;
if(ask(rec,oth[i])) break;
}while(rec.size());
}
ve=oth;
}
for(int i=1;i<=2*n;++i){
if(p[i].size()==1){
if(vis[i]||vis[p[i][0]]) continue;
Answer(i,p[i][0]);
vis[i]=vis[p[i][0]]=1;
}
else{
for(int j=0;j<=2;++j){
if(ask(i,p[i][j],p[i][(j+1)%3])==1){
lov[i]=p[i][(j+2)%3];whlov[lov[i]]=i;
break;
}
}
}
}
for(int i=1;i<=2*n;++i){
if(vis[i]) continue;
for(int k=0;k<=2;++k){
if(p[i][k]==lov[i]||p[i][k]==whlov[i]) continue;
if(vis[p[i][k]]) continue;
vis[i]=vis[p[i][k]]=1;
Answer(i,p[i][k]);
break;
}
}
}