算法 \(1\):给定点集 \(S\),\(|S| = n\),其中有 \(m\) 个好点。每次可以询问指定点集中是否存在好点,求所有好点。询问次数 \(O(\min \{m \log n, n\})\)。
对 \(S\) 分治,若当前不存在好点则退出。每个好点被询问 \(\lceil \log n \rceil\) 次,分治次数不超过 \(2n - 1\)。
算法 \(2\):给定树 \(T\),其中有一个好点。每次可以询问保留指定边集后好点是否与根连通,求好点编号。询问次数 \(O(\log |T|)\)。
求出 \(T\) 的 dfs 序后二分即可。(保留两端点 dfs 序 \(\le mid\) 的边)
链
称 \(0 \to u\) 路径上的边集为 \(u\) 的 根链。
打乱点的顺序,依次插入点,维护每个已插入点 \(u\) 的根链 \(R_u\)。
插入点 \(u\) 时,二分出 \(u\) 在链上的后继 \(v\),询问删去 \(R_v \setminus R_{pre_v}\) 内每条边后 \(0\) 与 \(u\) 是否连通,\(R_u\) 容易计算。
询问次数 \(O(n \log n)\)。
树
打乱点的顺序,依次插入点,将树分为若干条链。维护每条链的点集、边集、链顶父亲所在的链,并将链按插入顺序从小到大编号。设当前链并为 \(E\)。
插入点 \(u\) 时,考虑保留 \(E\) 时 \(0\) 与 \(u\) 是否连通。
- 若连通,使用 算法 \(2\) 二分出 \(u\) 所在的链,将 \(u\) 插入到这条链的点集。
- 若不连通,使用 算法 \(1\) 找出 \(u\) 根链上不在 \(E\) 中的边集 \(E_u\)。新建一条链,点集为 \(\{u\}\),考虑已插入链的并 \(T_0\),使用 算法 \(2\) 确定这条链链顶父亲所在的链。
求出链后,按编号从小到大还原链。使用 算法 \(2\) 求出链顶父亲的编号,使用链的做法求出点的顺序。
询问次数 \(O(n \log n)\)。
连通图
- 找出所有树边。枚举每个点 \(u\),每次二分最长前缀使删去后 \(0\) 与 \(u\) 连通,下一条边即为树边,然后删去前缀与新树边重新查找。
- 使用树的做法根据树边还原生成树的形态。
- 每次删去叶子 \(u\),同时求出与 \(u\) 相连的非树边信息。使用 算法 \(1\) 找出与 \(u\) 相连的所有非树边编号。对于每条非树边使用 算法 \(2\) 求出另一端点。
询问次数 \(O(m \log m)\)。
#include "graph.h"
#include <bits/stdc++.h>
using i64 = long long;
constexpr int M = 600;
using Set = std::vector<int>;
using Info = std::bitset<M>;
Set from(const Info &s) {
Set t;
for (int i = s._Find_first(); i < int(s.size()); i = s._Find_next(i)) {
t.push_back(i);
}
return t;
}
Info into(const Set &s) {
Info t;
for (auto i : s) {
t.set(i);
}
return t;
}
int n, m;
bool query(int u, const Info &s) {
std::vector<int> t(m);
for (int i = 0; i < m; ++i) {
t[i] = s[i];
}
return query(u, t);
}
Info flip(const Info &s) {
Info t;
for (int i = 0; i < m; ++i) {
t[i] = !s[i];
}
return t;
}
Set getSeq(const Set &fa) {
std::vector<Set> adj(fa.size());
for (int i = 1; i < int(fa.size()); ++i) {
if (fa[i] != -1) {
adj[fa[i]].push_back(i);
}
}
Set seq;
auto dfs = [&](auto self, int u) -> void {
seq.push_back(u);
for (auto v : adj[u]) {
self(self, v);
}
};
dfs(dfs, 0);
return seq;
}
template <typename F>
Info ask1(Set s, F &&f) {
Info res;
auto work = [&](auto self, int l, int r) -> void {
if (l >= r || f(Set(s.begin() + l, s.begin() + r))) {
return;
}
if (r - l == 1) {
res.set(s[l]);
return;
}
int m = (l + r) / 2;
self(self, l, m);
self(self, m, r);
};
work(work, 0, int(s.size()));
return res;
}
template <typename F>
int ask2(const Set &s, F &&f) {
int lo = 0, hi = int(s.size()) - 1;
while (lo < hi) {
int mid = (lo + hi) / 2;
if (f(Set(s.begin(), s.begin() + mid + 1))) {
hi = mid;
} else {
lo = mid + 1;
}
}
return s[lo];
}
std::mt19937 rnd;
std::vector<int> fa, fw;
void solveChain(Info pre, Set nodes, Info edges, int root) {
std::shuffle(nodes.begin(), nodes.end(), rnd);
struct Node {
int u;
Info e;
};
std::vector<Node> s{Node{root, pre}, Node{-1, pre | edges}};
for (auto u : nodes) {
auto it = std::partition_point(s.begin(), s.end(), [&](const Node &x) {
return !query(u, x.e);
});
Info e = std::prev(it)->e;
for (auto v : from(it->e ^ std::prev(it)->e)) {
auto q = it->e;
q.reset(v);
if (!query(u, q)) {
e.set(v);
}
}
s.insert(it, Node{u, e});
}
for (int i = 1; i < int(s.size()) - 1; ++i) {
auto e = from(s[i].e ^ s[i - 1].e);
fa[s[i].u] = s[i - 1].u;
fw[s[i].u] = e[0];
}
}
void solveTree(Info edges) {
Set nodes(n - 1);
std::iota(nodes.begin(), nodes.end(), 1);
std::shuffle(nodes.begin(), nodes.end(), rnd);
struct Chain {
Set nodes;
Info edges;
};
std::vector<Chain> s{Chain{Set{0}, Info{}}};
Set seq{0};
Info cur;
for (auto u : nodes) {
if (query(u, cur)) {
int bel = ask2(seq, [&](const Set &a) {
Info x;
for (auto v : a) {
x |= s[v].edges;
}
return query(u, x);
});
s[bel].nodes.push_back(u);
} else {
auto e = ask1(from(edges ^ cur), [&](const Set &a) {
return query(u, edges ^ into(a));
});
int f = ask2(seq, [&](const Set &a) {
Info x;
for (auto v : a) {
x |= s[v].edges;
}
return query(u, e | x);
});
cur |= e;
seq.insert(std::next(std::find(seq.begin(), seq.end(), f)), int(s.size()));
s.push_back(Chain{Set{u}, e});
}
}
cur.reset();
for (int i = 1; i < int(s.size()); ++i) {
int root = ask2(getSeq(fa), [&](const Set &a) {
auto x = s[i].edges;
for (auto v : a) {
if (v) {
x.set(fw[v]);
}
}
return query(s[i].nodes[0], x);
});
solveChain(cur, s[i].nodes, s[i].edges, root);
cur |= s[i].edges;
}
}
std::vector<std::pair<int, int>> solve(int n, int m) {
::n = n, ::m = m;
fa.assign(n, -1);
fw.assign(n, -1);
Info tree;
for (int u = 1; u < n; ++u) {
auto s = from(flip(tree));
int l = 0;
while (l < int(s.size())) {
int lo = l, hi = int(s.size());
while (lo < hi) {
int mid = (lo + hi) / 2;
auto x = into(Set(s.begin() + mid + 1, s.end()));
if (query(u, tree | x)) {
lo = mid + 1;
} else {
hi = mid;
}
}
if (lo < int(s.size())) {
tree.set(s[lo++]);
}
l = lo;
}
}
solveTree(tree);
std::vector<std::pair<int, int>> ans(m);
auto cur = tree, rest = flip(tree);
auto seq = getSeq(fa);
while (seq.size() > 1) {
int u = seq.back();
seq.pop_back();
ans[fw[u]] = {fa[u], u};
cur.reset(fw[u]);
auto e = ask1(from(rest), [&](const Set &a) {
return !query(u, cur | into(a));
});
for (auto i : from(e)) {
int v = ask2(seq, [&](const Set &a) {
Info x;
x.set(i);
for (auto v : a) {
if (v) {
x.set(fw[v]);
}
}
return query(u, x);
});
ans[i] = {u, v};
}
rest ^= e;
}
return ans;
}
标签:Info,std,Set,return,游戏,int,题解,图上,auto
From: https://www.cnblogs.com/jrjyy/p/18013556