1. 程式人生 > 其它 >UOJ461 新年的Dog劃分【圖論,互動】

UOJ461 新年的Dog劃分【圖論,互動】

這是一道互動題

互動庫有 \(n\) 個點 \(m\) 條邊的簡單無向連通圖,至多 \(2000\) 次詢問一個邊集 \(E'\),互動庫告訴你把 \(E'\) 中的邊去掉之後圖是否連通。求這張圖是不是二分圖,如果是則求一側。

\(2\le n\le 200\)


\(n^2\) 次詢問就可以把整個圖求出來,拿到了丟人的 \(37\) 分。

又忘了,連通無向圖->生成樹。

可以 bfs 把生成樹求出來,每次找出當前點連出的所有邊,可以直接大力二分,最後求出染色方案並 check 一下。

check 染色方案就列舉一條樹邊斷掉,並把除樹邊外的異色邊斷掉,如果連通說明不合法。

總詢問次數大約 \((\lceil\log_2n\rceil+2)n\)

#include<bits/stdc++.h>
#include"graph.h"
#define PB emplace_back
using namespace std;
typedef vector<int> VI;
typedef pair<int, int> pii;
const int N = 203;
int Q[N], fr, re;
VI q, ans, G[N];
vector<pii> now, tmp, edg;
bool col[N], vis[N], fal[N][N], tre[N][N];
int work(int u){
	if(q.empty()) return -1;
	int l = 0, r = q.size()-1, md;
	auto qry = [&](int r){
		tmp = now;
		for(int i = 0;i <= r;++ i)
			tmp.PB(u, q[i]);
		return query(tmp);
	};
	if(qry(r)){
		for(int i = 0;i <= r;++ i){
			fal[u][q[i]] = fal[q[i]][u] = true;
			now.PB(u, q[i]);
		}
		return -1;
	}
	while(l < r)
		qry(md = l+r>>1) ? l = md+1 : r = md;
	for(int i = 0;i < l;++ i){
		fal[u][q[i]] = fal[q[i]][u] = true;
		now.PB(u, q[i]);
	}
	return q[l];
}
void dfs(int x, int f){
	for(int v : G[x]) if(v != f){
		col[v] = !col[x]; dfs(v, x);
	}
}
VI check_bipartite(int n){
	vis[0] = true; Q[re++] = 0;
	while(fr < re){
		int u = Q[fr]; q.resize(0);
		for(int i = 0;i < n;++ i)
			if(!vis[i] && !fal[u][i]) q.PB(i);
		int v = work(u);
		if(v == -1){++fr; continue;}
		Q[re++] = v;
		tre[u][v] = tre[v][u] = vis[v] = true;
		G[u].PB(v); G[v].PB(u); edg.PB(u, v);
	}
	dfs(0, 0);
	tmp.resize(0);
	for(int i = 0;i < n;++ i) if(!col[i])
		for(int j = 0;j < n;++ j) if(col[j] && !tre[i][j])
			tmp.PB(i, j);
	tmp.PB();
	for(int x = 0;x < n-1;++ x){
		tmp.back() = edg[x]; 
		if(query(tmp)) return VI();
	}
	for(int i = 0;i < n;++ i) if(!col[i]) ans.PB(i);
	return ans;
}