首先有一个暴力的做法,将任意两个点判断,可以得到与之相关的1或3只变色龙:1只是两只变色龙相互喜欢,那么剩下那只就是颜色相同;3只从3只选2只并和自己判断一次,结果为1的那次剩下的那个就是他喜欢的,然后将所有喜欢关系删掉后剩下的就是颜色相同
但这样一开始需要$o(n^2)$次的判断,考虑优化,如果将点划分成若干个集合,每一个集合内部没有特殊关系就可行了,然后就可以再集合中二分来查找了,那么直接对前i-1个点构成的图染成2种颜色(4种颜色容易超过次数),分别进行二分查找即可,次数是$o(nlogn)$的,常数要注意(4种颜色的要注意要取编号最小的颜色,不然会被卡)
1 #include "chameleon.h" 2 #include <bits/stdc++.h> 3 using namespace std; 4 #define N 1005 5 vector<int>v,p[11],vec[N]; 6 int vis[N],to[N],ans[N][2]; 7 bool pd(vector<int> &k,int l,int r,int x){ 8 p[4].clear(); 9 p[4].push_back(x); 10 for(int i=l;i<=r;i++)p[4].push_back(k[i]); 11 return Query(p[4])<p[4].size(); 12 } 13 void find(vector<int> &a,int l,int r,int i){ 14 int rr=r; 15 while (1){ 16 r=rr; 17 if ((vec[i].size()==3)||(!pd(a,l,r,i)))return; 18 while (l<r){ 19 int mid=(l+r>>1); 20 if (pd(a,l,mid,i))r=mid; 21 else l=mid+1; 22 } 23 vec[i].push_back(a[l]); 24 vec[a[l]].push_back(i); 25 l++; 26 } 27 } 28 void Solve(int n){ 29 n*=2; 30 for(int i=1;i<=n;i++){ 31 int flag=4; 32 for(int j=0;j<4;j++) 33 if ((vec[i].size()==3)||(!pd(p[j],0,p[j].size()-1,i)))flag=min(flag,j); 34 else find(p[j],0,p[j].size()-1,i); 35 p[flag].push_back(i); 36 } 37 int t=0; 38 for(int i=1;i<=n;i++){ 39 if (vec[i].size()<3)continue; 40 vis[i]=1; 41 for(int j=0;j<2;j++) 42 for(int k=j+1;k<3;k++){ 43 v.clear(); 44 v.push_back(i); 45 v.push_back(vec[i][j]); 46 v.push_back(vec[i][k]); 47 if (Query(v)==1){ 48 to[i]=vec[i][3-j-k]; 49 j=k=3; 50 } 51 } 52 if (!to[i])to[i]=vec[i][0]; 53 } 54 for(int i=1;i<=n;i++) 55 if (vis[i]){ 56 for(int j=0;j<3;j++) 57 if (vec[i][j]==to[i])vec[i][j]=0; 58 for(int j=0;j<3;j++) 59 if (vec[to[i]][j]==i)vec[to[i]][j]=0; 60 } 61 for(int i=1;i<=n;i++) 62 if (vis[i]) 63 for(int j=0;j<3;j++) 64 if ((vec[i][j])&&(vec[i][j]<i)&&(vec[vec[i][j]].size()==3)){ 65 ans[++t][0]=i; 66 ans[t][1]=vec[i][j]; 67 } 68 for(int i=1;i<=n;i++) 69 if (vec[i].size()==1){ 70 if ((vec[vec[i][0]].size()==1)&&(i>vec[i][0]))continue; 71 ans[++t][0]=i; 72 ans[t][1]=vec[i][0]; 73 } 74 for(int i=1;i<=n/2;i++)Answer(ans[i][0],ans[i][1]); 75 }