[lnsyoj2234/luoguP9323]玩具设计
题意
原题链接
给定一个 个点的无向图,你可以进行不超过 次查询操作 ,每次操作可以查询在版本 中, 两个点之间是否连通,若连通,则返回当前版本号,否则新建一个版本,在该新版本中将这两个点之间连一条边,并返回新版本的版本号。要求构造出一个无向图,使得对于任意两个点,这两个点之间的连通情况与原版本中这两个点之间的连通情况相同。
赛时 并查集 65PTS
最简单的算法即为 的枚举。
事实上,如果两个点已经连通,就没有必要再次查询了;同理,如果一个的连通的任意一个点和另一个点不连通,也没有必要再次查询了。
因此,使用并查集维护连通性,并记录某一点不连通的所有点即可。
赛后
赛时的代码通过改变枚举顺序,可以 AC 此题。但仍有被卡可能。
类似于并查集的思想,我们可以记录与点 连通的最小的点 ,在输出时,我们只需要将 与 连边即可()
显然,每个点有两种情况:
- 点 不与 的任意点相连通,单独成为一个连通块
- 点 与 中的至少一点相连通
我们可以记录点 连通的最小的版本号 ,显然,。
若 ,由于版本 中, 已经连接,因此 单独成为一个连通块,即情况 。
否则, 不会单独成为一个连通块,即情况 。
对于情况 ,由定义,显然可得 ;
对于情况 ,在 构成的版本序列中,可以分为两段,前一段中, 不与 连通,此时任取这一段中的一个 ,都有 ;后一段中, 与 连通,此时任取这一段中的一个 ,都有 。
由此可得,在后一段中的第一个 即为 与 连通的第一个版本,因此,。显然我们可以使用二分通过 次查询找到这个 ;
不过如果这样直接二分的话,对于特殊数据,可能会略微超出一点。因此,我们还需要进行一点优化:因为 是与 连通的编号最小点,因此,对于任意两点 ,其中 ,若 ,则一定存在 。因此,我们不需要二分整个 ,只需要二分其中所有下标 满足 的版本即可。
代码
#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);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现