可恶!就差……一步了…… 【反贼阵亡】
最近做题好像经常这样,只差最后一步想不出来(x
首先考虑一个 \(O(n^2)\) 次交互的做法:将每对 \((i, j)\) 询问一次,如果返回值为 \(1\),只会有以下几种情况:
将所有对询问一次之后,对返回值为 \(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;
}
原文:https://www.cnblogs.com/suwakow/p/12672246.html