传送门
做法:两两查询一次,那么给颜色数为 1 的数对连边,那么一个点可以连出 1 或 3 条边
对于一条的可以直接确定,对于 3 条的,假设当前位,它喜欢的为
,喜欢它的为
,颜色相同的为
,那么当且仅当查询
时为 1,如果我们给
- 利用二分图的性质,对于已知的边,可以把
的点分成两个集合,集合内均无边,然后在集合中分治,查询次数
,并且这个可以用并查集维护
复杂度,查询次数
#include "chameleon.h"
//#include "grader.cpp"
#include<bits/stdc++.h>
#define
#define
using namespace std;
cs int N = 1050;
int fa[N], c[N], id[N], d[N];
vector<int> G[N];
int fnd(int x){ return x==fa[x]?x:fnd(fa[x]); }
int fndc(int x){ return x==fa[x]?0:c[x]^fndc(fa[x]); }
void mrg(int u, int v){
int fx=fnd(u), fy=fnd(v);
if(d[fx]>d[fy]) swap(fx,fy), swap(v,u);
if(fx==fy) return;
fa[fx]=fy; d[fy]=max(d[fy],d[fx]+1);
c[fx]=fndc(u)^fndc(v)^1;
}
bool work_e(vector<int> S, int l, int r, int u, bool flg=0){
if(G[u].size()==3) return false;
if(l>r) return false;
if(!flg){
vector<int> T; T.pb(u);
for(int i=l; i<=r; i++) T.pb(S[i]);
int c = Query(T); if(c>r-l+1) return false;
}
if(l==r){
G[u].pb(S[l]);
G[S[l]].pb(u);
mrg(u,S[l]); return true;
}
int mid = (l+r) >> 1;
bool t = work_e(S,l,mid,u,false);
work_e(S,mid+1,r,u,!t);
return true;
}
void Solve(int n){
srand(time(0));
for(int i=1; i<=n+n; i++) id[i]=i;
random_shuffle(id+1,id+n+n+1);
for(int i=1; i<=n+n; i++) fa[i]=i, c[i]=0;
for(int i=1; i<=n+n; i++){
vector<int> A, B;
for(int j=1; j<i; j++) if(G[id[j]].size()!=3)
fndc(id[j])?B.pb(id[j]):A.pb(id[j]);
work_e(A,0,A.size()-1,id[i]);
work_e(B,0,B.size()-1,id[i]);
}
static int mp[N][N], vis[N][N];
for(int i=1; i<=n+n; i++) if(G[i].size()==1&&!vis[i][G[i][0]])
vis[i][G[i][0]]=vis[G[i][0]][i]=1, Answer(i,G[i][0]);
for(int i=1; i<=n+n; i++) if(G[i].size()>1){
assert(G[i].size()==3);
for(int u=0; u<3; u++) if(G[G[i][u]].size()==3)
for(int v=u+1; v<3; v++) if(G[G[i][v]].size()==3){
vector<int> S; S.pb(i), S.pb(G[i][u]), S.pb(G[i][v]);
if(Query(S)==1){
++mp[i][G[i][u]], ++mp[i][G[i][v]];
++mp[G[i][u]][i], ++mp[G[i][v]][i];
break;
}
}
}
for(int i=1; i<=n+n; i++)
for(int j=i+1; j<=n+n; j++)
if(mp[i][j]==2) Answer(i,j);
}