首页 > 其他分享 >图上的游戏 题解

图上的游戏 题解

时间:2024-02-11 21:55:59浏览次数:19  
标签:Info std Set return 游戏 int 题解 图上 auto

「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;
}

标签:Info,std,Set,return,游戏,int,题解,图上,auto
From: https://www.cnblogs.com/jrjyy/p/18013556

相关文章

  • CF1715E Long Way Home题解
    题解注意到\(k\)是一个很小的数,我们考虑分层图是否可做,这时航线有\(n^2\)条,我们可能会建出\((k+1)m+kn^2\)条边,空间会炸掉,然而单单从分层图的角度来优化,是困难的。对于\(m=0\)的情况。考虑\(\text{dp}\),定义\(dp_{i,j}\)表示乘坐不超过\(i\)次航班到达\(j\)的最......
  • CF1928E 题解
    \(\textbf{ProblemStatement}\)给定\(n,x,y,s\),构造长度为\(n\)的序列\(a\),满足:\(a_1=x\)。\(\foralli\in[2,n],a_i=a_{i-1}+y\)或者\(a_i=a_{i-1}\bmody\)。\(\sum\limits_{i=1}^na_i=s\)。给出构造或报告无解。\(\sumn,\sums\le......
  • 题解 AT_mujin_pc_2016_c【オレンジグラフ】
    本文中点的编号从\(0\)开始。显然,题目中要求橙色的边构成极大的二分图。枚举二分图左右部分别有哪些点。特别地,钦定\(0\)号点是左部点。将所有跨左右部的边染为橙色,如果所有点通过橙色的边连通,就得到了一组合法的解;如果不连通,显然可以将更多的边染成橙色,使得所有点连通。//......
  • 题解 ABC336G【16 Integers】
    萌萌BEST定理练习题。赛时几乎做出来了,但写挂了,现在在火车上没事干就给补了。考虑建图,图中共有\(8\)个节点,节点的编号是\((\mathbb{Z}/2\mathbb{Z})^3\)的每个元素。对于每个四元组\((i,j,k,l)\in(\mathbb{Z}/2\mathbb{Z})^4\),在图中连\(X_{i,j,k,l}\)条\((i,j,k)\to(j......
  • 洛谷 P1795 无穷的序列 题解
    题目传送门题目大意:给定整数\(a\),判断\(a\)是否属于数列\(1,2,4,7,11\cdots\)。多测。1.暴力枚举(90pts)不难发现,该数列除第一项外第\(n\)项比第\(n-1\)项多\(n-1\)。故暴力枚举\(n\),计算数列的每一项,判断是否与\(a\)相等,大于\(a\)就break。多测加记忆化,用mx......
  • 杀人游戏
    注意,在调查前应该有一个定下来的顺序,就是不管这张图是哪一种都按这个顺序进行调查由题意,这\(n\)个人当中一定有一个人是杀手那么就相当于有\(n\)张图,其中每张图都有且仅有一个黑点(剩余都是白点),且这些图的黑点都不同(黑点就是杀手)首先我们肯定要保证知道杀手,所以一定只会询问入度......
  • P5524 [Ynoi2012] NOIP2015 充满了希望 题解
    题目链接:充满了希望一开始以为是传统老题,结果看到有个交换单修操作,ODT这题试了下,应该\(fake\)了,毕竟有单修,很难保证之前的\(log\)级复杂度。有些较为智慧的解法确实不好思考,说个很简单的做法,这里没有问颜色数,而是问的颜色具体情况,那就比之前的很多题简单太多了。颜色的具体......
  • AtCoder-abc340_f题解
    题意简述给定整数\(x,y\),询问是否存在整数\(a,b\),满足:\(-10^{18}\lea,b\le10^{18}\)。由\((0,0),(x,y),(a,b)\)三点构成的三角形面积为\(1\)。如果存在,输出任意一组;否则输出-1。思路先假设\(x,y,a,b\)都是正数。那么图大概是这样:此时红色三角形的面积就......
  • P4183 [USACO18JAN] Cow at Large P 题解
    Description贝茜被农民们逼进了一个偏僻的农场。农场可视为一棵有\(N\)个结点的树,结点分别编号为\(1,2,\ldots,N\)。每个叶子结点都是出入口。开始时,每个出入口都可以放一个农民(也可以不放)。每个时刻,贝茜和农民都可以移动到相邻的一个结点。如果某一时刻农民与贝茜相遇了......
  • 【译】如果金钱是一个电子游戏,以下是各个关卡
    原作:蒂姆·丹宁引言:了解自己当前所处的层级,然后理解上一层级是什么样的,这样你就能够逐渐实现财务中等水平。 图片来源-中途赚钱感觉不公平。在金钱游戏中,不同层次的人们彼此传授金钱建议。然而,这很少有效,因为一个处在低层级的人接受来自高层级的人的建议时无法产生共鸣。......