【loj 3274】「JOISC 2020 Day2」变色龙之恋【分治】

传送门

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;
		}
	}
}
posted @ 2021-01-09 15:41  cjTQX  阅读(149)  评论(0编辑  收藏  举报