[lnsyoj2234/luoguP9323]玩具设计

题意

原题链接
给定一个 \(n\) 个点的无向图,你可以进行不超过 \(max\_ops\) 次查询操作 \(\text{Connected}(x,a, b)\),每次操作可以查询在版本 \(x\) 中,\(a,b\) 两个点之间是否连通,若连通,则返回当前版本号,否则新建一个版本,在该新版本中将这两个点之间连一条边,并返回新版本的版本号。要求构造出一个无向图,使得对于任意两个点,这两个点之间的连通情况与原版本中这两个点之间的连通情况相同。

赛时 并查集 65PTS

最简单的算法即为 \(O(n^2)\) 的枚举。
事实上,如果两个点已经连通,就没有必要再次查询了;同理,如果一个的连通的任意一个点和另一个点不连通,也没有必要再次查询了。
因此,使用并查集维护连通性,并记录某一点不连通的所有点即可。

赛后

赛时的代码通过改变枚举顺序,可以 AC 此题。但仍有被卡可能。
类似于并查集的思想,我们可以记录与点 \(i\) 连通的最小的 \(f_i\),在输出时,我们只需要将 \(f_i\)\(i\) 连边即可(\(f_i \ne i\)
显然,每个点有两种情况:

  1. \(i\) 不与 \(1\sim i-1\) 的任意点相连通,单独成为一个连通块
  2. \(i\)\(1\sim i-1\) 中的至少一点相连通

我们可以记录点 \(1 \sim i\) 连通的最小的版本号 \(rt_i\),显然,\(rt_i = \text{Connected}(rt_{i-1}, 1, i)\)
\(rt_i \ne rt_{i-1}\),由于版本 \(rt_{i-1}\) 中,\(1\sim i-1\) 已经连接,因此 \(i\) 单独成为一个连通块,即情况 \(1\)
否则,\(i\) 不会单独成为一个连通块,即情况 \(2\)

对于情况 \(1\),由定义,显然可得 \(f_i = i\)
对于情况 \(2\),在 \(rt\) 构成的版本序列中,可以分为两段,前一段中,\(i\) 不与 \(1\sim i-1\) 连通,此时任取这一段中的一个 \(rt_j\),都有 \(\text{Connected}(rt_j, j, i) \ne rt_j\);后一段中, \(i\)\(1 \sim i-1\) 连通,此时任取这一段中的一个 \(rt_j\),都有 \(\text{Connected}(rt_j, j, i) = rt_j\)
由此可得,在后一段中的第一个 \(rt_j\) 即为 \(i\)\(1\sim i-1\) 连通的第一个版本,因此,\(f_i = j\)。显然我们可以使用二分通过 \(\log n\) 次查询找到这个 \(j\);

不过如果这样直接二分的话,对于特殊数据,可能会略微超出一点。因此,我们还需要进行一点优化:因为 \(f_i\) 是与 \(i\) 连通的编号最小点,因此,对于任意两点 \(i,j\),其中 \(f_i=i,f_j\ne j\),若 \(f_i=f_j\),则一定存在 \(i<j\)。因此,我们不需要二分整个 \(rt\),只需要二分其中所有下标 \(i\) 满足 \(f_i = i\) 的版本即可。

代码

#include "akai.h"

using namespace std;

const int N = 205;

int f[N], rt[N];
int s[N], idx = 0;

void ToyDesign(int n, int max_ops){
    rt[1] = 0;
    f[1] = 1;
    s[ ++ idx] = 1;
    for (int i = 2; i <= n; i ++ ){
        rt[i] = Connected(rt[i - 1], i - 1, i);
        if (rt[i - 1] != rt[i]) f[i] = i, s[ ++ idx] = i;
        else {
            int l = 1, r = idx;
            while (l < r){
                int mid = (l + r) >> 1;
                if (Connected(rt[s[mid]], s[mid], i) == rt[s[mid]]) r = mid;
                else l = mid + 1;
            }
            f[i] = s[l];
        }
    }

    vector<pair<int, int>> result;
    for (int i = 1; i <= n; i ++ ) 
        if (f[i] != i) result.push_back({i, f[i]});
    DescribeDesign(result);
}
posted @ 2024-08-02 16:25  是一只小蒟蒻呀  阅读(30)  评论(0编辑  收藏  举报