[LOJ3274]变色龙之恋

可恶!就差……一步了…… 【反贼阵亡】

最近做题好像经常这样,只差最后一步想不出来(x

首先考虑一个 \(O(n^2)\) 次交互的做法:将每对 \((i, j)\) 询问一次,如果返回值为 \(1\),只会有以下几种情况:

  1. \(i, j\) 颜色相同;
  2. \(i\) 喜欢 \(j\)
  3. \(j\) 喜欢 \(i\)

将所有对询问一次之后,对返回值为 \(1\)\((i, j)\) 之间连边,发现对于一个 \(i\),一定会恰有 \(1\)\(3\) 条边。恰有 \(1\) 条边的情况是喜欢关系形成了二元环,在询问时相互抵消了(我酸了),因此这样的点颜色可以直接确定。

对于恰有 \(3\) 条边的情况,设分别为 \((i, v_0), (i, v_1), (i, v_2)\)。依次询问 \(\{i, v_0, v_1, v_2\} \setminus \{v_i\}\),发现只有 \(v_i\) 恰好是上面的情况 \(2\) 的时候,返回值为 \(1\)

因此,我们唯一确定了 \(i\) 喜欢的是谁,只要再知道喜欢 \(i\) 的是谁,就可以求出 \(i\) 的颜色了。

发现这种单向的喜欢关系必然是若干个环组成的(然而现实生活中只会有内向树),只需要顺着 \(v_i\) 向下递归即可。整个递归过程中,环上的每个点都可以一次询问求出喜欢的是谁和对应的颜色,最终回到 \(i\) 时,也可以求出 \(i\)。这个过程是 \(O(n)\) 的,不超过 \(3n\) 次询问即可。

现在唯一的问题是如何找到每个点的连边。再考虑一下每个点预先知道性别的情况:

\(S=\{i| y_i=0\}\),现在考虑对于每个 \(y_i=1\)\(i\),求出它与 \(S\) 中点的连边,考虑二分实现。

具体来说,设 \(\operatorname{solve}(S', x)\) 表示查找集合 \(S'\)\(x\) 之间的连边,若询问 \(S'\cup \{x\}\) 时,返回值为 \(|S'|+1\),则 \(S'\) 内部元素与 \(x\) 显然没有连边,可以直接返回。否则,若 \(|S'|=1\),可以直接将对应的点与 \(x\) 连边。否则,直接将 \(S'\) 分为尽量相等的两部分,递归处理。通过精细实现和一些剪枝,可以做到不超过 \(3n\log_2 n\) 次询问。

然后我就不会了(少女百度中……

由于所有边都存在于异性之间,因此上面知道性别的意义,实际上在于提前确定了二分图中每个点的归属。

实际上知道每个点在二分图的哪边并没有多少用,我们关心的只是,\(\operatorname{solve}(S', x)\) 中,\(S'\) 内部没有连边。因此我们可以从 \(1\)\(2n\) 扫描,对每个 \(i\),求出所有 \((i, j), j<i\)。初始时,由于未确定任何连边,可以给每个点任意指定颜色;每次连边时,暴力将不对的颜色修改过来即可。(甚至不需要并查集.jpg)

由于原图是二分图,并且所有 \([1, i-1]\) 内部的连边都已经确定,我们可以直接将 \([1, i-1]\) 分为两个内部没有连边的集合,套用上面的做法即可。复杂度不变,但常数略大,需要一些剪枝才能通过。

#include <bits/stdc++.h>
#include "chameleon.h"
#define R register
#define mp make_pair
#define ll long long
#define pii pair<int, int>
using namespace std;
const int mod = 998244353, N = 1100;

int col[N], lv[N], vis[N], mat[N];
vector<int> to[N];

inline int addMod(int a, int b) {
	return (a += b) >= mod ? a - mod : a;
}

inline ll quickpow(ll base, ll pw) {
	ll ret = 1;
	while (pw) {
		if (pw & 1) ret = ret * base % mod;
		base = base * base % mod, pw >>= 1;
	}
	return ret;
}

template <class T>
inline void read(T &x) {
	x = 0;
	char ch = getchar(), w = 0;
	while (!isdigit(ch)) w = (ch == '-'), ch = getchar();
	while (isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
	x = w ? -x : x;
	return;
}

void dfs1(int now) {
	for (auto &v : to[now]) {
		if (col[v] != col[now]) continue;
		col[v] ^= 1, dfs1(v);
	}
	return;
}

inline void addEdg(int x, int y) {
	if (col[x] == col[y]) col[y] ^= 1, dfs1(y);
	to[x].push_back(y), to[y].push_back(x);
	return;
}

vector<int> resolve(vector<int> s, int x, int flag = 0) {
	vector<int> t;
	if (!s.size()) return vector<int>();
	s.push_back(x);
	if (!flag) {
		int w = s.size() - Query(s);
		if (!w) return vector<int>();
	}
	s.pop_back();
	if (s.size() == 1) return s;
	int m = s.size() >> 1;
	while (m--) t.push_back(s.back()), s.pop_back();
	vector<int> l = resolve(t, x);
	if (l.size() < 3) {
		vector<int> r = resolve(s, x, l.empty());
		while (r.size()) l.push_back(r.back()), r.pop_back();
	}
	return l;
}

void dfs2(int now, int rt) {
	if (now == rt) return;
	if (vis[now]) {
		for (R int i = 0; i <= 2; ++i)
			if (mat[to[now][i]] != now && lv[to[now][i]] != now)
				lv[now] = to[now][i];
		return dfs2(lv[now], rt);
	}
	vis[now] = 1;
	vector<int> s;
	s.push_back(now);
	for (R int i = 0; i <= 2; ++i)
		if (lv[to[now][i]] == now) s.push_back(to[now][i]);
	for (R int i = 0; i <= 2; ++i)
		if (lv[to[now][i]] != now) {
			s.push_back(to[now][i]);
			break;
		}
	if (Query(s) == 1) {
		for (R int i = 0; i <= 2; ++i)
			if (lv[to[now][i]] != now) {
				mat[now] = to[now][i];
				mat[to[now][i]] = now;
				vis[to[now][i]] = 1;
				Answer(now, mat[now]);
				break;
			}
		for (R int i = 2; ~i; --i)
			if (lv[to[now][i]] != now) {
				lv[now] = to[now][i];
				break;
			}
	}
	else {
		for (R int i = 2; ~i; --i)
			if (lv[to[now][i]] != now) {
				mat[now] = to[now][i];
				mat[to[now][i]] = now;
				vis[to[now][i]] = 1;
				Answer(now, mat[now]);
				break;
			}
		for (R int i = 0; i <= 2; ++i)
			if (lv[to[now][i]] != now) {
				lv[now] = to[now][i];
				break;
			}
	}
	dfs2(lv[now], rt);
	return;
}

void Solve(int n) {
	for (R int i = 1; i <= 2 * n; ++i) {
		vector<int> s;
		for (R int j = 1; j < i; ++j)
			if (col[j]) s.push_back(j);
		vector<int> fl = resolve(s, i);
		s.clear();
		for (R int j = 1; j < i; ++j)
			if (!col[j]) s.push_back(j);
		vector<int> fr = resolve(s, i);
		for (auto &v : fl) addEdg(v, i);
		for (auto &v : fr) addEdg(v, i);
	}
	for (R int i = 1; i <= 2 * n; ++i) {
		if (vis[i]) continue;
		if (to[i].size() == 1) {
			vis[to[i][0]] = 1;
			mat[i] = to[i][0], mat[to[i][0]] = i;
			Answer(i, to[i][0]);
			continue;
		}
		vector<int> s;
		s.push_back(i);
		s.push_back(to[i][0]), s.push_back(to[i][1]);
		if (Query(s) == 1) {
			lv[i] = to[i][2];
			dfs2(to[i][2], i);
			if (vis[i]) continue;
			int v = lv[to[i][0]] == i ? to[i][1] : to[i][0];
			Answer(i, v), vis[v] = 1, mat[i] = v, mat[v] = i;
			continue;
		}
		s[2] = to[i][2];
		if (Query(s) == 1) {
			lv[i] = to[i][1];
			dfs2(to[i][1], i);
			if (vis[i]) continue;
			int v = lv[to[i][0]] == i ? to[i][2] : to[i][0];
			Answer(i, v), vis[v] = 1, mat[i] = v, mat[v] = i;
			continue;
		}
		lv[i] = to[i][0];
		dfs2(to[i][0], i);
		if (vis[i]) continue;
		int v = lv[to[i][1]] == i ? to[i][2] : to[i][1];
		Answer(i, v), vis[v] = 1, mat[i] = v, mat[v] = i;
	}
	return;
}
posted @ 2020-04-10 11:28  suwakow  阅读(400)  评论(0编辑  收藏  举报
Live2D