「NOI2019」I 君的探险 部分分 题解

题目链接

(代码在最后)

测试点 1

这个点只需要读懂题目就能乱搞过去。

无数种方法中的一个:

  • 修改 0 号点
  • 分别查询 1、2 号点,只要被修改了就代表它与 0 有边
  • 若1、2 点不同时与 0 有边连接,代表 1、2 点之间有边
  • 时间复杂度: O ( 1 ) O(1) O(1)
  • 修改次数:1
  • 查询次数:2
  • 检查次数:0

测试点 2 ~ 5

依次修改 0 ≤ i &lt; n − 1 0\leq i&lt;n-1 0i<n1,依次查询 i &lt; j &lt; n i&lt;j&lt;n i<j<n 并记录,假如 j j j 相比上次有变化,代表它与 i i i 有边。

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)
  • 修改次数: O ( n ) O(n) O(n)
  • 查询次数: O ( n 2 ) O(n^2) O(n2)
  • 检查次数:0

测试点 6~9

M = N / 2 M=N/2 M=N/2 和性质 A 可以看出所有点是一对一对的。

再看 L m , L q L_m,L_q Lm,Lq,发现它们大概是 O ( n log ⁡ n ) O(n\log n) O(nlogn) 的。所以我们考虑 n log ⁡ n n\log n nlogn 的方法。

二分?分治?排序? log ⁡ \log log 数据结构?

这道题(我采用的)其实是二进制位。

枚举编号的每个二进制位,修改这一位为 1 的所有节点。接着查询所有节点,我们根据这个节点编号在这个二进制位上的值分类讨论,可以得到与其配对的节点的编号在这位上的值。

假如不想记录每一次的查询结果的话,在每一位求完后直接把修改过的节点改回去即可。

  • 时间复杂度: O ( n log ⁡ n ) O(n\log n) O(nlogn)
  • 修改次数: O ( n log ⁡ n ) O(n\log n) O(nlogn)
  • 查询次数: O ( n log ⁡ n ) O(n\log n) O(nlogn)
  • 检查次数:0

测试点 10~11

这是一棵父节点编号小于子节点的树。

我们考虑类似上一个 Subtask 的做法,但是按编号从小到大枚举时一边修改,一边查询,这样每个节点在被查询到时只会受其父亲影响。

最后得到每个节点(0 除外)的父亲编号,挨个输出即可。

  • 时间复杂度: O ( n log ⁡ n ) O(n\log n) O(nlogn)
  • 修改次数: O ( n log ⁡ n ) O(n\log n) O(nlogn)
  • 查询次数: O ( n log ⁡ n ) O(n\log n) O(nlogn)
  • 检查次数:0

测试点 12~14

这是一条链。

模仿上上个 subtask 的方法,我们可以得到与其连接的两个节点的异或和。

接着修改 0 号点,再 O ( n ) O(n) O(n) 扫一遍找到与 0 连接的2(1)个点。接着从这两个点入手就可以求出来了。

  • 时间复杂度: O ( n log ⁡ n ) O(n\log n) O(nlogn)
  • 修改次数: O ( n log ⁡ n ) O(n\log n) O(nlogn)
  • 查询次数: O ( n log ⁡ n ) O(n\log n) O(nlogn)
  • 检查次数:(可用可不用)

测试点 14~25

咕咕咕

代码

LOJ 格式化后的代码:

#include "explore.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
namespace my_exp_2_5 {

bool li[510];
}

namespace my_exp_6_9 {

int p[200000];
}

namespace my_exp_10_11 {

int p[200000];
}

namespace my_exp_12_14 {

int p[200000];
}

void explore(int N, int M) {
    if (N == 3 && M == 2) {
        modify(0);
        int q1 = query(1), q2 = query(2);
        if (q1)
            report(0, 1);
        if (q2)
            report(0, 2);
        if (q1 != q2)
            report(1, 2);
        return;
    }
    if (N <= 500 && N % 10 == 0) {
        using namespace my_exp_2_5;
        for (int i = 0; i < N - 1; i++) {
            modify(i);
            for (int j = i + 1; j < N; j++) {
                int q = query(j);
                if (q != li[j]) {
                    report(i, j);
                    li[j] = q;
                }
            }
        }
        return;
    }
    if (N % 10 == 8) {
        using namespace my_exp_6_9;
        for (int i = 0; (1 << i) <= N; i++) {
            for (int j = 0; j < N; j++) {
                if (!((j >> i) & 1))
                    continue;
                modify(j);
            }
            for (int j = 0; j < N; j++) {
                if ((j >> i) & 1) {
                    p[j] |= ((!query(j)) << i);
                } else {
                    p[j] |= (query(j) << i);
                }
            }
            for (int j = 0; j < N; j++) {
                if (!((j >> i) & 1))
                    continue;
                modify(j);
            }
        }
        for (int i = 0; i < N; i++) {
            if (p[i] < i)
                report(i, p[i]);
        }
        return;
    }
    if (N % 10 == 7) {
        using namespace my_exp_10_11;
        for (int i = 0; (1 << i) <= N; i++) {
            for (int j = 0; j < N; j++) {
                p[j] |= (query(j) << i);
                if ((j >> i) & 1)
                    modify(j);
            }
            for (int j = 0; j < N; j++) {
                if ((j >> i) & 1)
                    modify(j);
            }
        }
        for (int i = 1; i < N; i++) {
            report(i, p[i]);
        }
        return;
    }
    if (N % 10 == 6) {
        using namespace my_exp_12_14;
        for (int i = 0; (1 << i) <= N; i++) {
            for (int j = 0; j < N; j++) {
                if (!((j >> i) & 1))
                    continue;
                modify(j);
            }
            for (int j = 0; j < N; j++) {
                if ((j >> i) & 1) {
                    p[j] |= ((!query(j)) << i);
                } else {
                    p[j] |= (query(j) << i);
                }
            }
            for (int j = 0; j < N; j++) {
                if (!((j >> i) & 1))
                    continue;
                modify(j);
            }
        }
        modify(0);
        int c1 = 0, c2 = 0;
        for (int i = 1; i < N; i++) {
            if (query(i)) {
                if (c1)
                    c2 = i;
                else
                    c1 = i;
            }
        }
        int last = 0, now = c1;
        for (int i = 1; i < N; i++) {
            report(last, now);
            if (check(now))
                break;
            last = (last ^ p[now]);
            int t = last;
            last = now;
            now = t;
        }
        if (c2) {
            last = 0, now = c2;
            for (int i = 1; i < N; i++) {
                report(last, now);
                if (check(now))
                    break;
                last = (last ^ p[now]);
                int t = last;
                last = now;
                now = t;
            }
        }
        return;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值