#P8493. [IOI 2022] 数字电路

[IOI 2022] 数字电路

背景

由于技术限制,请不要使用 C++ 14 (GCC 9) 提交本题。

这是一道交互题,你只需要实现代码中要求的函数。

你的代码不需要引用任何额外的头文件,也不需要实现 main 函数。

由于本题数据点过多,结合洛谷评测技术实现情况,本题将不按照题给 Subtask 评分。

题目描述

有一个数字电路,由编号为从 00 到 N+M−1N + M - 1 的 N+MN + M 个门组成。其中,00 到 N−1N - 1 号门是阈值门,而 NN 到 N+M−1N + M - 1 号门是输入门。

除 00 号门之外的每个门都是恰好一个某阈值门的输入。具体来说,对于每个满足 1≤i≤N+M−11 \le i \le N + M - 1 的 ii,门 ii 是门 P[i]P[i] 的一个输入,其中 0≤P[i]≤N−10 \le P[i] \le N-1。重要的是,我们保证 P[i]<iP[i] \lt i 成立。此外,我们假设有 P[0]=−1P[0] = -1。每个阈值门有一个或多个的输入。输入门没有任何输入。

每个门都有一个状态,取 00 或 11。输入门的初始状态由一个包含 MM 个整数的数组 AA 给定。也就是说,对于每个满足 0≤j≤M−10 \le j \le M - 1 的 jj ,输入门 N+jN + j 的初始状态为 A[j]A[j]。

每个阈值门的状态取决于它的输入的状态,具体如下。首先,每个阈值门会被指定一个阈值参数。对于一个有 cc 个输入的阈值门,其所指定的参数必须是 11 到 cc 之间的某个整数(包括 11 和 cc)。随后,对于一个参数为 pp 的阈值门,如果它的输入中至少有 pp 个门的状态为 11,则当前阈值门的状态为 11,否则状态为 00。

例如,假设有 N=3N = 3 个阈值门和 M=4M = 4 个输入门。其中,门 00 的输入为门 11 和门 66,门 11 的输入为门 22、44 和 55,门 22 仅有的输入为门 33。

上述例子的说明可见下图。

假设输入门 33 和 55 的状态为 11,而门 44 和 66 的状态为 00。假设阈值门 22、11、00 被指定的参数分别为 11、22、22。在这种情况下,门 22 的状态为 11,门 11 的状态为 11 ,门 00 的状态为 00。下面给出了参数赋值以及状态的示意图。状态为 11 的门被标记为黑色。

输入门的状态将会经历 QQ 次更新。每次更新用两个整数 LL 和 RR 来描述 (N≤L≤R≤N+M−1N \le L \le R \le N + M - 1) ,表示翻转所有编号在 LL 和 RR 之间(包括 LL 和 RR)的输入门的状态。这就是说,对于所有满足 L≤i≤RL \le i \le R 的 ii,输入门 ii 的状态如果为 00,则会被翻转为11;如果状态为 11,则会被翻转为 00。每个门被翻转后将会一直保持在新状态,直到在后续某次更新中被翻转。

你的目标是,计算每次更新后有多少种阈值门参数的赋值方案,使得门 00 的状态为 11。当有至少一个阈值门的参数不同时,两种参数赋值方案被认为是不同的。由于方案数可能较大,你需要计算它对 1  000  002  0221\;000\;002\;022 取模的结果。

注意,在上面的例子中,共有 66 种不同的对阈值门参数进行赋值的方案,因为门 00、11、22 分别有 22、33、11 个输入。在这 66 种方案里面,有 22 种参数赋值方案使得门 00 的状态为 11。

输入格式

你的任务是实现下述两个函数。

void init(int N, int M, int[] P, int[] A)
  • NN: 阈值门的数量。
  • MM:输入门的数量。
  • PP: 一个长度为 N+MN + M 的数组,给出阈值门的输入。
  • AA: 一个长度为 MM 的数组,给出输入门的初始状态。
  • 这个函数被调用恰好一次,且发生在函数 count_ways 的所有调用之前。
int count_ways(int L, int R)
  • LL, RR:编号在 LL 和 RR 之间的输入门的状态将会被翻转。
  • 这个函数应首先执行所规定的更新,然后返回使得门 00 的状态为 11 的参数赋值方案的方案数对 1  000  002  0221\;000\;002\;022 取模的结果。
  • 这个函数会被调用恰好 QQ 次。

输出格式

考虑如下的函数调用序列:

init(3, 4, [-1, 0, 1, 2, 1, 1, 0], [1, 0, 1, 0])

题面描述中已经给出了对这个例子的解释。

count_ways(3, 4)

这次调用翻转了门 33 和 44 的状态,也就是说,门 33 的状态变成 00,门 44 的状态变成 11。下面给出了两种可行的参数赋值方案,可以使得门 00 的状态为 11 。

方案 11 方案 22

在所有其他的参数赋值方案中,门 00 的状态为 00。因此,函数应返回 22。

count_ways(4, 5)

这次调用翻转了门 44 和 55 的状态。其结果是,所有输入门的状态均为 00,而且对于所有的参数赋值方案,门 00 的状态均为 00。因此,函数应返回 00。

count_ways(3, 6)

这次调用将所有输入门的状态置为 11。其结果是,对于所有参数赋值方案,门 00 的状态均为 11。因此,函数应返回 66。

提示

约束条件

  • 1≤N,M≤1051 \le N, M \le 10^5;
  • 1≤Q≤1051 \le Q \le 10^5;
  • P[0]=−1P[0] = -1;
  • 0≤P[i]<i0 \le P[i] \lt i 且 P[i]≤N−1P[i] \le N - 1(对于所有满足 1≤i≤N+M−11 \le i \le N + M - 1 的 ii);
  • 每个阈值门至少有一个输入(对于所有满足 0≤i≤N−10 \le i \le N - 1 的 ii,存在某个下标 xx 满足 i<x≤N+M−1i \lt x \le N + M - 1 且 P[x]=iP[x] = i);
  • 0≤A[j]≤10 \le A[j] \le 1(对于所有满足 0≤j≤M−10 \le j \le M - 1的 jj);
  • N≤L≤R≤N+M−1N \le L \le R \le N + M - 1。

子任务

  1. (2 分)N=1N = 1,M≤1000M \le 1000,Q≤5Q \le 5;
  2. (7 分)N,M≤1000N, M \le 1000,Q≤5Q \le 5,每个阈值门都有恰好两个输入;
  3. (9 分)N,M≤1000N, M \le 1000,Q≤5Q \le 5;
  4. (4 分)M=N+1M = N + 1,M=2zM = 2^z(对于某个正整数 zz), P[i]=⌊i−12⌋P[i] = \lfloor\frac{i - 1}{2}\rfloor(对于所有满足 1≤i≤N+M−11 \le i \le N + M - 1 的 ii),L=RL = R;
  5. (12 分)M=N+1M = N + 1,M=2zM = 2^z(对于某个正整数 zz),P[i]=⌊i−12⌋P[i] = \lfloor\frac{i - 1}{2}\rfloor(对于所有满足1≤i≤N+M−11 \le i \le N + M - 1的 ii);
  6. (27 分)每个阈值门都恰好有两个输入;
  7. (28 分)N,M≤5000N, M \le 5000;
  8. (11 分)没有额外的约束条件。

评测程序示例

评测程序示例读取如下格式的输入:

  • 第 11 行: N  M  QN \; M \; Q;
  • 第 22 行: P[0]  P[1]  …  P[N+M−1]P[0] \; P[1] \; \ldots \; P[N + M - 1];
  • 第 33 行: A[0]  A[1]  …  A[M−1]A[0] \; A[1] \; \ldots \; A[M - 1];
  • 第 4+k4 + k 行(0≤k≤Q−10 \le k \le Q - 1): 第 kk 次更新对应的 L  RL \; R。

评测程序示例按照如下格式打印你的答案:

  • 第 1+k1 + k 行(0≤k≤Q−10 \le k \le Q - 1): count_ways 函数对第 kk 次更新的返回值。

约定

题面在给出函数接口时,会使用一般性的类型名称 void、bool、int、int[](数组)和 union(bool, int[])。

在 C++ 中,评测程序会采用适当的数据类型或实现,如下表所示:

void bool int int[]
void bool int std::vector<int>
union(bool, int[]) 数组 a 的长度
std::variant<bool, std::vector<int>> a.size()

C++ 语言里,std::variant 定义在 <variant> 头文件中。 一个返回类型为 std::variant<bool, std::vector<int>> 的函数可以返回一个 bool 或一个 std::vector<int>。 以下示例代码给出了三个返回 std::variant 的函数,它们都能正常工作:

std::variant<bool, std::vector<int>> foo(int N) {
    return N % 2 == 0;
}

std::variant<bool, std::vector<int>> goo(int N) {
    return std::vector<int>(N, 0);
}

std::variant<bool, std::vector<int>> hoo(int N) {
    if (N % 2 == 0) {
        return false;
    }

    return std::vector<int>(N, 0);
}