1. 程式人生 > 實用技巧 >【loj 3274】「JOISC 2020 Day2」變色龍之戀【分治】

【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;
		}
	}
}