P7211
对于两个点
- 若
x,y 颜色相同,则x,y 之间连一条无向边,记为x \leftrightarrow y 。 - 若
x 喜欢y ,则连一条x \to y 的有向边。
先不考虑询问的限制次数。
如果我们询问所有的点对
如果为
容易发现,当答案为
进一步可以发现,每个点的度数都是
若询问出来
若询问出来
不妨设为
尽管我们无法得知是哪种,但是我们可以以此确定
通过这种方法,我们可以确定所有的有向边,那么剩下的边就是无向边,即颜色相同的点。
这个做法的询问次数为
考虑优化这个过程。
我们可以通过
方法为,从
找到极大独立集之后,对于独立集之外的点,在独立集中二分找到所有边。
此时还剩下独立集之外的点中的边没有找到,那么重复上述过程,直到所有剩下的点都是一个独立集。
由于每个点的度数最多为
又由于每找到一条边需要一次二分,而边数是
这样我们就可以用 random_shuffle。
inline int get(int x, vi p) {
int l = 0, r = p.size() - 1;
while (l < r) {
int mid = (l + r) >> 1;
vi q(1, x);
for (int i = l; i <= mid; i++) q.pb(p[i]);
if (Query(q) != (int)q.size()) r = mid;
else l = mid + 1;
}
return p[l];
}
inline int ask(int x, int y, int z) {
vi p(3);
p[0] = x, p[1] = y, p[2] = z;
return Query(p);
}
void Solve(int n) {
srand(time(0));
vi A, B;
for (int i = 1; i <= (n << 1); i++) B.pb(i);
random_shuffle(B.begin(), B.end());
vector<vi> e(n << 1 | 1);
while (B.size()) {
vi S = B, v(n << 1 | 1);
A.clear(), B.clear();
for (int x : S) {
A.pb(x);
if (Query(A) != (int)A.size()) A.pop_back(), B.pb(x);
else v[x] = 1;
}
for (int i : B) {
vi p = A;
p.pb(i);
do {
p.pop_back();
int k = get(i, p);
e[k].pb(i), e[i].pb(k);
for (int &x : p)
if (x == k) swap(x, p.back());
p.pop_back();
p.pb(i);
} while (Query(p) != (int)p.size());
}
}
vi v(n << 1 | 1);
vector<map<int, bool>> f(n << 1 | 1);
for (int x = 1; x <= (n << 1); x++)
if (e[x].size() == 3u) {
int y = e[x][0], z = e[x][1], w = e[x][2];
if (ask(x, y, w) == 1) swap(z, w);
else if (ask(x, z, w) == 1) swap(y, w);
f[x][w] = f[w][x] = 1;
}
for (int x = 1; x <= (n << 1); x++)
if (!v[x])
for (int y : e[x])
if (!f[x][y]) v[x] = v[y] = 1, Answer(x, y);
}