博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

Goodbye Wuxu.B.新年的Dog划分(交互 二分 二分图)

题目链接


官方题解写得很详细,我竟然看懂了

Subtask1:
暴力的话,猜可以\(2^n\)枚举点集\(A,B\),将除了\(A,B\)之间的边全部删掉,然后询问。一定有至少一组\(A,B\)返回连通。
设二分图两边的点集分别是\(S,T\),能猜到此时\(S=A,T=B\)。假设\(S\cap A\neq\emptyset\)\(T\cap A\neq\emptyset\),那\(A,B\)连通说明\(S,T\)通过同一个点连通,显然不会,因此\(T\cap A=\emptyset\)。同理\(S\cap B\neq\emptyset\)。所以有\(S=A,T=B\)。询问次数\(O(2^n)\)

Subtask2:
如果图是二分图,考虑找到原图的一棵生成树,然后就可以黑白染色找出\(S\)了。
尝试依次删掉图中所有边。如果删掉一条边时由连通变为不连通,说明它在生成树上,保留这条边,否则删掉这条边。最终一定剩下\(n-1\)条边,就是原图的生成树。
询问次数\(O(\frac{n(n-1)}{2})\)

Subtask3:
找生成树时一条一条枚举边太慢了,可以二分。如果删掉\(1\sim mid\)中的边图仍连通,就删掉,否则找到那条必须边保留。
这样的事件会进行\(n-1\)次,每次需要二分\(\log\frac{n(n-1)}{2}\approx2\log n\)次。
黑白染色后得到\(S,T\),只靠生成树是可以连接\(S,T\)的。如果图不是二分图,那么把\(S,T\)之间的非树边全删掉,一定存在某一条树边,使得删掉这条树边后图仍连通。
总询问次数\(2(n-1)\log n+n-1\)

Subtask4:
考虑对点二分,把二分时\(2\)的常数去掉。
枚举每个点,二分它与哪个点之间的边必须保留即可。
询问次数\(n\log n\)


//4955ms	1584kb
#include <cstdio>
#include <cctype>
#include <vector>
#include <cstring>
#include <algorithm>
#define pb push_back
#define fir first
#define sec second
#define mp std::make_pair
#define pr std::pair<int,int>

#include "graph.h"

const int N=233;

int Enum,H[N],nxt[N<<1],to[N<<1],col[N];
bool tag[N][N];
std::vector<pr> tr;

inline void AE(int u,int v)
{
	to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
	to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum;
}
void DFS(int x)
{
	for(int i=H[x],v; i; i=nxt[i])
		if(!col[v=to[i]]) tag[x][v]=tag[v][x]=1, tr.push_back(mp(x,v)), col[v]=col[x]^1, DFS(v);
}
std::vector<int> check_bipartite(int n)
{
	std::vector<pr> q;
	for(int x=0; x+1<n; ++x)
	{
		int now=x+1;
		while(now<n)
		{
			for(int i=now; i<n; ++i) q.push_back(mp(x,i));
			if(query(q)) break;
			for(int i=now; i<n; ++i) q.pop_back();
			int l=now,r=n-1,mid;
			while(l<r)
			{
				int mid=l+r>>1;
				for(int i=l; i<=mid; ++i) q.push_back(mp(x,i));
				if(query(q)) l=mid+1;
				else r=mid;
				for(int i=l; i<=mid; ++i) q.pop_back();
			}
			AE(x,l), now=l+1;
		}
	}
	col[0]=2, DFS(0);
	std::vector<int> ans; q.clear();
	for(int i=0; i<n; ++i)//Check if it's a Bipartite Graph
		for(int j=i+1; j<n; ++j)
			if(col[i]!=col[j] && !tag[i][j]) q.push_back(mp(i,j));
	for(int i=0,l=tr.size(); i<l; ++i)
	{
		q.push_back(tr[i]);
		if(query(q)) return ans;
		q.pop_back();
	}
	for(int i=0; i<n; ++i) if(col[i]==2) ans.push_back(i);
	return ans;
}

67分代码:

#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
#define pb push_back
#define fir first
#define sec second
#define mp std::make_pair
#define pr std::pair<int,int>

#include "graph.h"

const int N=233,M=N*N/2;

int Enum,H[N],nxt[N<<1],to[N<<1],col[N];
pr e[M];
bool tag[M];

inline void AE(int u,int v)
{
	to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
	to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum;
}
void DFS(int x)
{
	for(int i=H[x],v; i; i=nxt[i])
		if(!col[v=to[i]]) col[v]=3-col[x], DFS(v);
}
std::vector<int> check_bipartite(int n)//2(n-1)logn+n-1
{
	int m=0;
	for(int i=0; i<n; ++i)
		for(int j=i+1; j<n; ++j) e[++m]=mp(i,j);
	for(int T=n-1,now=1; T--; )
	{
		int l=now,r=m,mid;
		while(l<r)
		{
			int mid=l+r>>1;
			std::vector<pr> q;
			for(int i=1; i<=mid; ++i) if(!tag[i]) q.push_back(e[i]);
			if(query(q)) l=mid+1;
			else r=mid;
		}
		AE(e[r].fir,e[r].sec), tag[r]=1, now=r+1;
	}
	std::vector<int> ans;
	col[0]=1, DFS(0);
	for(int i=1; i<=m; ++i)//Check if it's a Bipartite Graph
		if(tag[i])
		{
			std::vector<pr> q; q.push_back(e[i]);
			for(int j=1; j<=m; ++j)
				if(!tag[j] && col[e[j].fir]!=col[e[j].sec]) q.push_back(e[j]);
			if(query(q)) return ans;
		}
	for(int i=0; i<n; ++i) if(col[i]==1) ans.push_back(i);
	return ans;
}
posted @ 2019-02-20 09:45  SovietPower  阅读(379)  评论(0编辑  收藏  举报