图上的游戏 题解

「2020 集训队论文」图上的游戏

算法 \(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\) 是否连通。

  1. 若连通,使用 算法 \(2\) 二分出 \(u\) 所在的链,将 \(u\) 插入到这条链的点集。
  2. 若不连通,使用 算法 \(1\) 找出 \(u\) 根链上不在 \(E\) 中的边集 \(E_u\)。新建一条链,点集为 \(\{u\}\),考虑已插入链的并 \(T_0\),使用 算法 \(2\) 确定这条链链顶父亲所在的链。

求出链后,按编号从小到大还原链。使用 算法 \(2\) 求出链顶父亲的编号,使用链的做法求出点的顺序。

询问次数 \(O(n \log n)\)

连通图

  1. 找出所有树边。枚举每个点 \(u\),每次二分最长前缀使删去后 \(0\)\(u\) 连通,下一条边即为树边,然后删去前缀与新树边重新查找。
  2. 使用树的做法根据树边还原生成树的形态。
  3. 每次删去叶子 \(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;
}
posted @ 2024-02-11 21:50  jrjyy  阅读(53)  评论(0)    收藏  举报