可恶!就差……一步了…… 【反贼阵亡】
最近做题好像经常这样,只差最后一步想不出来(x
首先考虑一个 (O(n^2)) 次交互的做法:将每对 ((i, j)) 询问一次,如果返回值为 (1),只会有以下几种情况:
- (i, j) 颜色相同;
- (i) 喜欢 (j);
- (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') 分为尽量相等的两部分,递归处理。通过精细实现和一些剪枝,可以做到不超过 (3nlog_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;
}