[lnsyoj2234/luoguP9323]玩具设计

题意

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

赛时 并查集 65PTS

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

赛后

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

  1. i 不与 1i1 的任意点相连通,单独成为一个连通块
  2. i1i1 中的至少一点相连通

我们可以记录点 1i 连通的最小的版本号 rti,显然,rti=Connected(rti1,1,i)
rtirti1,由于版本 rti1 中,1i1 已经连接,因此 i 单独成为一个连通块,即情况 1
否则,i 不会单独成为一个连通块,即情况 2

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

不过如果这样直接二分的话,对于特殊数据,可能会略微超出一点。因此,我们还需要进行一点优化:因为 fi 是与 i 连通的编号最小点,因此,对于任意两点 i,j,其中 fi=i,fjj,若 fi=fj,则一定存在 i<j。因此,我们不需要二分整个 rt,只需要二分其中所有下标 i 满足 fi=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 @   是一只小蒟蒻呀  阅读(37)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示