IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 「JOISC 2017 Day 3」自然公园 题解 -> 正文阅读

[数据结构与算法]「JOISC 2017 Day 3」自然公园 题解

因为感觉网上题解很少,所以想来写一点感性+理性混合的题解qwq

而且犯了很多弱智错误)

考虑我们可以以以下形式确定每一条边:

我们维护一个包含0号节点的连通块 每次挑选一个点 x x x,满足 x x x 有和连通块直接相连的边

我们的任务就是:如何确定这样一条边?

直接扫肯定是不可接受的,所以我们采用二分的形式:

确定一个序(dfs/bfs)均可,然后在这个序上进行二分,二分出哪个前缀是满足联通的最小前缀(就是只包含这个前缀里的节点时,可以满足仍然有直接连边)

这样,前缀里的最后一个点,就是和这个点有直接连边的点

然后删去这个直接相连的点,会分出若干连通块,再检验 x x x 和这些连通块是否有直接连边

考虑为什么检验次数是对的,首先显然会有 M l o g n Mlogn Mlogn 次检验(不过这个log应该比较小)

然后考虑,删了一个点之后会分出若干连通块,就算立马返回,也要一次check

那么对于菊花来说,复杂度是否正确呢?

因为题目限制最多7条边,所以最多分出六个连通块,也就是总复杂度应该是 7 M l o g 7Mlog 7Mlog

鉴于很难每个点都跑到上届,所以可过)

那么另一个问题 我们如何找到一个 x x x 呢?

事实上 我们每次可以随机联块外一个点 x x x,随机的必要性在于防止被精心构造的数据多卡出一个 N l o g Nlog Nlog的check

然后对于这个 x x x 我们可以找到它和连通块之间的一点 y y y

但注意 我们应该标记连通块外的所有点,当以它去找路径中点的时候,应该把它删掉,理由有2:

1.他已经不可能成为其他的点的,路径上的必须点了(再搜完它前面的点之后 它会直接成为连通块,而它不可能成为它前面的点的必须点)

2.避免连通块之间胡搜,可能会出现(测试出的)连通块之间互搜的情况

#include "park.h"
#include <bits/stdc++.h>
using namespace std;
int const N = 1410;
struct stu1 {
    int y, nex;
} e[N * 5];
int in[N], out[N], t[N], lin[N], tot, cnt, tim, vis[N], q[N], num, pl[N], n;
bool check(int x) {
    in[x] = 1;
    int op = Ask(0, x, in);
    in[x] = 0;
    return op;
}
void bfs(int x) {
    int l = 1, r = 1;
    q[1] = x;
    ++tim;
    int dq, y;
    tot = 0;
    vis[x] = tim;

    while (l <= r) {
        dq = q[l++];
        t[++tot] = dq;

        for (int i = lin[dq]; i; i = e[i].nex) {
            y = e[i].y;

            if (in[y] && vis[y] < tim) {
                q[++r] = y;
                vis[y] = tim;
            }
        }
    }
}
bool ask(int x, int y, int lim) {
    memset(pl, 0, sizeof(pl));

    for (int i = 1; i <= lim; i++)
        pl[t[i]] = 1;

    pl[y] = 1;
    return Ask(min(x, y), max(x, y), pl);
}
void link(int x, int y) {
    e[++cnt] = (stu1) {
        y, lin[x]
    };
    lin[x] = cnt;
}
void extend(int ro, int x) {
    bfs(ro);//删了一个点之后 会变成若干连通块 (等等 对于菊花来说 复杂度为什么是对的... 因为只有七条边所以复杂度是对的)

    if (!ask(ro, x, tot))
        return;

    int mid, l = 1, r = tot; //找到序最小的一个直接相连的位置

    while (l < r) {
        mid = (l + r) >> 1;

        if (ask(ro, x, mid))
            r = mid;
        else
            l = mid + 1;
    }

    int z = t[l], dfn = tim;
    in[z] = 0;

    for (int i = lin[z]; i; i = e[i].nex) {
        int y1 = e[i].y; //注意 我这里写的和参考程序有出入 参考程序开多个p的目的就在于这里

        if (in[y1] && vis[y1] <= dfn)
            extend(y1, x);
    }

    in[z] = 1;
    link(z, x);
    link(x, z);
    Answer(min(x, z), max(x, z));
}
void work(int
          x) { //把它调出out集合的原因是: 虽然不会出现xy直接互搜的情况 但是可能会出现循环节互搜的情况..
    //而且调出去一定不错 目的是找到和连通块直接连接的点 可以确定这个点不和连通块直接连接
    out[x] = 0;
    int mid, l, r, d = 0;
    tot = 0;

    while (!check(x)) {
        d = 0; //while可能会跑多次 要清d

        for (int i = 0; i < n; i++)
            if (in[i])
                t[++d] = i;

        l = d + 1;

        for (int i = 0; i < n; i++)
            if (out[i])
                t[++d] = i;

        r = d;

        while (l < r) {
            mid = (l + r) >> 1;

            if (ask(0, x, mid))
                r = mid;
            else
                l = mid + 1;
        }

        work(t[l]);
    }

    extend(0, x);
    num++;
    in[x] = 1; //找出所有x向连通块里连的边
}
void Detect(int T, int
            N) { //我不是很懂为什么要rand一个点出来 所以我不rand  盲猜一下 应该是怕被精心构造的数据卡之类的?
    in[0] = 1;
    num = 1;
    n = N; //还是需要rand的 不然最劣可以跑到8Mlog左右(大概)

    for (int i = 1; i < N; i++)
        out[i] = 1;

    int x;

    while (num < N) {
        x = rand() % N;

        while (in[x]) {
            x = rand() % N; //最坏n^2次 总之应该是能随完的)
        }

        work(x); //处理i号点和连通块里所有点的连边 然后把它加连通块里
    }
}
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-05-04 07:30:14  更:2022-05-04 07:30:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 5:54:45-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码