Hidden Bipartite Graph

liyixin發表於2024-10-06

Hidden Bipartite Graph

題意

互動題。有一個 \(n \le 600\) 的圖,你可以詢問至多 \(20000\) 次。每次問一個點集 \(S\),返回滿足兩個端點都在 \(S\) 中的邊的個數。你需要判斷這個圖是不是二分圖,如果是,則分別輸出左部和右部的點,否則按順序輸出任意一個奇環。

思路

先判斷二分圖。一個十分巧妙的方法是,先找出一棵生成樹,然後染色,然後再擴充判斷是否是二分圖。這樣做是因為生成樹是好找的(圖保證連通),而且樹是特別的二分圖。

現在我們任意找出一棵生成樹。我們發現判斷一個點 \(x\) 和一個點集 \(S\) 有沒有連邊是容易的,只需要先問 \(S+x\),然後再問 \(S\),然後相減。

設已經連通的點集為 \(T\),剩餘點集為 \(S\)。初始 \(T=\{1\},S=2 \sim n\)

找樹邊的時候我們逮住 \(T\) 的一個點 \(x\),然後二分的方式找出和它相連的所有點,並把這些點加入 \(T\),然後 \(x\) 的使命就結束了了,可以把它從 \(T\) 刪除,因為它無法再用來更新邊了。

這樣每個點加入生成樹最多要 \(2 \log n\) 次詢問,一共 \(2n \log n\) 左右,大概 \(10800\)

然後我們就找到了一棵生成樹。然後給這棵樹黑白染色,就是奇數層染黑色,偶數層染白色。然後黑、白兩個點集分別查詢一下內部是否有邊,如果沒有,那麼這兩個點集就是二分圖的左右部,否則不存在二分圖。

如果不存在二分圖,我們還需要找一個奇環。只要生成樹上奇數層和奇數層或者偶數層和偶數層有連邊,就行成了一個奇環。環包括書上路徑和非樹邊的那一條邊。因此我們對於每個奇數層的點(當然偶數層也同理可以),採用同樣的方式二分所有奇數層的點(除去它自己),先判斷一下有沒有連邊,如果有就找到一個和它有連邊的點。一共大概 \(2n+2 \log n\) 次詢問。完全足夠。

總共詢問次數大概 \(2 n \log n + 2n + 2 \log n\)。CF 上最大跑了 \(19307\) 次。

code

#include<bits/stdc++.h>
//#define LOCAL
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
using namespace std;
typedef long long ll;
const int N=1000;
int n;
vector<int> res;
int st[N],top;
int m;
int dep[N],fa[N];
vector<int> to[N];
int write(vector<int> &vec,int l,int r) {
	if(r-l+1<2) return 0;
	cout<<"? "<<r-l+1<<endl;
	rep(i,l,r) cout<<vec[i]<<' ';
	cout<<endl;
	cin>>m;
	return m;
}
int write(vector<int> &vec,int l,int r,int y) {
	if(r-l+1<=0) return 0;
	cout<<"? "<<r-l+1+1<<endl;
	rep(i,l,r) cout<<vec[i]<<' ';
	cout<<y<<endl;
	cin>>m;
	return m;
}
int write(int x,int y) {
	cout<<"? 2"<<endl;
	cout<<x<<' '<<y<<endl;
	cin>>m;
	return m;
}
vector<int> del;
void solve(int u,int l,int r) {
	if(l==r) {
		int m=write(u,res[l]);
		if(m) del.push_back(l),st[++top]=res[l],to[u].push_back(res[l]),to[res[l]].push_back(u);
		return;
	}
	int m=write(res,l,r,u)-write(res,l,r);
	if(!m) return;
	int mid=(l+r)>>1;
	solve(u,l,mid);solve(u,mid+1,r);
}
vector<int> lb,rb;
int col[N];
void dfs(int u) {
	if(col[u]==1) lb.push_back(u);
	else rb.push_back(u);
	for(int v:to[u]) {
		if(col[v]) continue;
		dep[v]=dep[u]+1;fa[v]=u;
		col[v]=col[u]==1?2:1;
		dfs(v);
	}
}
bool check() {
	if(lb.size()>=2) {
		cout<<"? "<<lb.size()<<endl;
		for(int u:lb) cout<<u<<' ';
		cout<<endl;
		cin>>m;
		if(m) return 0;
	}
	if(rb.size()>=2) {
		cout<<"? "<<rb.size()<<endl;
		for(int u:rb) cout<<u<<' ';
		cout<<endl;
		cin>>m;
		if(m) return 0;
	}
	return 1;
}
bool check(vector<int> &vec,int u) {
	return write(vec,0,vec.size()-1,u)-write(vec,0,vec.size()-1);
}
bool check(int u,vector<int> &vec,int l,int r) {
	return write(vec,l,r,u)-write(vec,l,r);
}
int find(int u,vector<int> &vec,int l,int r) {
	if(l==r) return vec[l];
	int mid=(l+r)>>1;
	if(check(u,vec,l,mid)) return find(u,vec,l,mid);
	else return find(u,vec,mid+1,r);
}
int main(){
	#ifdef LOCAL
	freopen("in.txt","r",stdin);
//	freopen("my.out","w",stdout);
	#endif
	cin>>n;
	rep(i,2,n) res.push_back(i);
	st[++top]=1;
	while(!res.empty()) {
		int u=st[top--];
		solve(u,0,(int)res.size()-1);
		vector<int> tmp;
		sort(del.begin(),del.end());
		int op=0;
		rep(i,0,(int)res.size()-1) {
			if(op<(int)del.size()&&i==del[op]) op++;
			else tmp.push_back(res[i]);
		}
		del.clear();
		res=tmp;
	}
	col[1]=1;
	dep[1]=1;
	dfs(1);
	if(check()) {
		sort(lb.begin(),lb.end());
		int k=unique(lb.begin(),lb.end())-lb.begin();
		cout<<"Y "<<k<<endl;
		for(int j=0;j<k;j++) cout<<lb[j]<<' ';
		cout<<endl;
	}else{
		rep(i,0,(int)lb.size()-1) {
			vector<int> tmp;
			rep(j,0,i-1) tmp.push_back(lb[j]);
			rep(j,i+1,(int)lb.size()-1) tmp.push_back(lb[j]);
			if(check(tmp,lb[i])) {
				int x=find(lb[i],tmp,0,tmp.size()-1);
				vector<int> lp,lp2;
				int y=lb[i];
				if(dep[x]<dep[y]) swap(x,y);
				while(dep[x]>dep[y]) {
					lp.push_back(x);
					x=fa[x];
				}
				while(x!=y) {
					lp.push_back(x),lp2.push_back(y);
					x=fa[x],y=fa[y];
				}
				lp.push_back(x);
				cout<<"N "<<lp.size()+lp2.size()<<endl;
				for(int u:lp) cout<<u<<' ';
				for(int k=lp2.size()-1;k>=0;k--) cout<<lp2[k]<<' ';
				cout<<endl;
				return 0;
			}
		}
		rep(i,0,(int)rb.size()-1) {
			vector<int> tmp;
			rep(j,0,i-1) tmp.push_back(rb[j]);
			rep(j,i+1,(int)rb.size()-1) tmp.push_back(rb[j]);
			if(check(tmp,rb[i])) {
				int x=find(rb[i],tmp,0,tmp.size()-1);
				vector<int> lp,lp2;
				int y=rb[i];
				if(dep[x]<dep[y]) swap(x,y);
				while(dep[x]>dep[y]) {
					lp.push_back(x);
					x=fa[x];
				}
				while(x!=y) {
					lp.push_back(x),lp2.push_back(y);
					x=fa[x],y=fa[y];
				}
				lp.push_back(x);
				cout<<"N "<<lp.size()+lp2.size()<<endl;
				for(int u:lp) cout<<u<<' ';
				for(int k=lp2.size()-1;k>=0;k--) cout<<lp2[k]<<' ';
				cout<<endl;
				return 0;
			}
		}
	}
}

相關文章