AtCoder Beginner Contest 367

题目传送门:AtCoder Beginner Contest 367

A - Shout Everyday

题意简述(*43

每天你都在 \(b\) 点钟时睡觉,在 \(c\) 点钟时起床。问你在 \(a\) 点钟时是否醒着。时间采用 24 小时制,且三个时间点互不相同。

数据范围:\(0 \le a, b, c < 24\)\(a, b, c\) 两两不同。

AC 代码
#include <iostream>
using std::cin, std::cout;

void Solve();
int main() {
    cin.sync_with_stdio(false);
    cin.tie(nullptr);
    Solve();
    return 0;
}

void Solve() {
    int a, b, c;
    cin >> a >> b >> c;
    if (b < c) b += 24;
    if (a < c) a += 24;
    cout << (c <= a && a <= b ? "Yes" : "No") << '\n';
}

关键在于:

  • \(b < c\),则你在零点钟时还醒着,故醒着的时段是一天中的前缀 \([0, b]\) 和后缀 \([c, 24]\)
  • \(b > c\),则你在零点钟时睡着了,故醒着的时段是一天中的一段区间 \([c, b]\)
  • 可以对这两种情况分别进行处理。

通过以 \(c\)(起床时刻)为基准,将 \(a, b\) 当小于 \(c\) 的时候加上 \(24\),总可以将判断转化为判断 \(a\) 是否在区间 \([c, b]\) 内的情况。

加上 \(24\) 了之后相当于 \(25\) 点钟相当于次日的凌晨 \(1\) 点钟。

感性理解:在日本的广播业和深夜营业的店铺中常用的 30 小时制时刻表示法。

时空复杂度为 \(\mathcal O(1)\)

B - Cut .0

题意简述(*43

以恰好精确到小数点后第三位的形式输入一个实数 \(x\),请你去掉不必要的末尾零以及小数点后输出。

数据范围:\(0 \le x < 100\)\(x\) 以恰好精确到小数点后第三位的形式给出。

AC 代码
#include <iostream>
using std::cin, std::cout;

void Solve();
int main() {
    cin.sync_with_stdio(false);
    cin.tie(nullptr);
    Solve();
    return 0;
}

void Solve() {
    double x;
    cin >> x;
    cout << x << '\n';
}

由于 C++ 的 std::ostream 的特质,在默认初始化后输出一个浮点数,会在保留 6 位有效位数后,(如果不触发科学计数法格式)自动进行末尾零和小数点的移除。再由于 \(x < 100\),即有效位数不足 5,且 \(x\) 的指数不会过小以至于触发科学计数法格式,故而在输入后,直接 std::cout << x << '\n'; 即可达成目的。

使用 std::printf("%g", x); 可以达成完全一致的目的。

具体而言,是因为 std::ostream 输出浮点数时使用了函数 std::num_put::put 进行,它调用了虚函数 std::num_put::do_put,在处理浮点数时,虚函数默认的实现,当 std::ios_base格式化标志(即 std::ios_base::flags())为默认设置(即仅通过调用函数 std::basic_ios::init 初始化后未改动)时,为如同以 %g转换说明符进行 C 风格输出得到的结果。

详细来说,格式化标志中的 std::ios_base::floatfield 管理浮点数的格式化方式,其中含有 std::ios_base::fixedstd::ios_base::scientific 两个标志,默认这两个标志都没有开启。当使用 std::fixed 开启后,行为就如同以 %f 的转换说明符进行 C 风格输出了。当然还有我们很熟悉的 std::setprecision 可以对精度进行调整(默认值是 6)。

时空复杂度为 \(\mathcal O(1)\)

C - Enumerate Sequences

题意简述(*234

给定长度为 \(n\) 的正整数序列 \((r_1, \ldots, r_n)\) 和正整数 \(k\)

以字典序从小到大的顺序输出所有满足 \(1 \le a_i \le r_i\)\(a_1 + \cdots + a_n\)\(k\) 的倍数的长度为 \(n\) 的整数序列 \((a_1, \ldots, a_n)\)

数据范围:\(n \le 8\)\(1 \le r_i \le 5\)\(2 \le k \le 10\)

AC 代码
#include <iostream>
using std::cin, std::cout;

#define F(i, a, b) for(int i = a; i <= (b); ++i)
#define F2(i, a, b) for(int i = a; i < (b); ++i)

void Solve();
int main() {
    cin.sync_with_stdio(false);
    cin.tie(nullptr);
    Solve();
    return 0;
}

const int MN = 8;

int n, k, r[MN];

int a[MN];
void DFS(int s) {
    if (s == n) {
        int sum = 0;
        F2(i, 0, n)
            sum += a[i];
        if (sum % k == 0)
            F2(i, 0, n)
                cout << a[i] << " \n"[i + 1 == n];
        return ;
    }
    F(v, 1, r[s]) {
        a[s] = v;
        DFS(s + 1);
    }
}

void Solve() {
    cin >> n >> k;
    F2(i, 0, n)
        cin >> r[i];
    DFS(0);
}

先不谈总和要是 \(k\) 的倍数的条件,序列的总数只有 \(\prod_{i = 1}^n r_i \le (\max r)^n \le 5^8 \le 39{,}0625\) 那么多,所以把所有序列列举出来在时间上是可以接受的。

另一方面讲,\(k\) 最大是 \(10\),大约只能排除掉九成的序列而还要留下一成,所以序列总数必然不能过多。

对于按字典序输出的要求,一个 DFS(深度优先搜索)足矣。我们需要在 DFS 到达边界后求和进行判断和输出。

时间复杂度为 \(\mathcal O((\max r)^n \cdot n)\),空间复杂度为 \(\mathcal O(n)\)

D - Pedometer

题意简述(*1037

环上有 \(n\) 个关键点,按顺时针以 \(1 \sim n\) 编号。

给定正整数 \(a_1, \ldots, a_n\)。从点 \(i\) 顺时针走 \(a_i\) 的距离才能到达顺时针方向的下一个点。

给定 \(m\),求满足从点 \(i\) 顺时针走到点 \(j\) 的距离为 \(m\) 的倍数的点对 \((i, j)\)(要求 \(i \ne j\))的数量。

数据范围:\(n \le 10^5\)\(a_i \le 10^9\)\(m \le 10^6\)

AC 代码
#include <iostream>
#include <vector>
using std::cin, std::cout;

#define F2(i, a, b) for(int i = a; i < (b); ++i)

void Solve();
int main() {
    cin.sync_with_stdio(false);
    cin.tie(nullptr);
    Solve();
    return 0;
}

using LL = long long;

void Solve() {
    int n, m;
    cin >> n >> m;
    std::vector<int> a(n);
    F2(i, 0, n)
        cin >> a[i];
    std::vector<int> b(m, 0);
    int s = 0;
    F2(i, 0, n) {
        s = (s + a[i]) % m;
        ++b[s];
    }
    int t = s;
    LL ans = 0;
    F2(i, 0, n) {
        s = (s + a[i]) % m;
        ++b[s];
        --b[(s - t + m) % m];
        ans += b[s] - 1;
    }
    cout << ans << '\n';
}

考虑断环为链,变为 \(2 n\) 个点,前 \(n\) 个只作辅助用,后 \(n\) 个作为关心的终点。记录下这些点的坐标(即从原点出发到达它们需要的距离)。

要问从多少个点出发到达点 \(i\) 的距离为 \(m\) 的倍数,就是在问从 \(i\) 往前数 \(n - 1\) 个点(不包含 \(i\) 本身),这些点中坐标与 \(i\) 的坐标模 \(m\) 同余的点的数量。

使用一个值域为 \([0, m)\) 的桶来统计余数,终点每向右移动一次时,就在桶中添加它当前坐标的余数,并删除它在绕一圈前坐标的余数。将桶中与当前相同的余数数量(但要减去 \(1\),因为本身不算)累加就得到答案。

时空复杂度为 \(\mathcal O(n + m)\)

E - Permute K times

题意简述(*1370

给定两个长度为 \(n\) 的整数序列 \((a_1, \ldots, a_n)\)\((x_1, \ldots, x_n)\),其中保证 \(1 \le x_i \le n\)

求经过以下变换 \(k\) 次后的序列 \(a\)

  • \(a\) 替换为 \(b\),其中序列 \((b_1, \ldots, b_n)\) 定义为 \(b_i = a_{x_i}\)

数据范围:\(n \le 2 \times 10^5\)\(k \le 10^{18}\)\(1 \le x_i \le n\)

AC 代码
#include <iostream>
#include <vector>
using std::cin, std::cout;

#define F(i, a, b) for(int i = a; i <= (b); ++i)

void Solve();
int main() {
    cin.sync_with_stdio(false);
    cin.tie(nullptr);
    Solve();
    return 0;
}

using LL = long long;

void Solve() {
    int n;
    LL k;
    cin >> n >> k;
    std::vector<int> x[60], a(n + 1);
    x[0].resize(n + 1);
    F(i, 1, n)
        cin >> x[0][i];
    F(j, 0, 58) {
        x[j + 1].resize(n + 1);
        F(i, 1, n)
            x[j + 1][i] = x[j][x[j][i]];
    }
    F(i, 1, n)
        cin >> a[i];
    F(i, 1, n) {
        int p = i;
        F(j, 0, 59)
            if (k >> j & 1)
                p = x[j][p];
        cout << a[p] << " \n"[i == n];
    }
}

注意到最后 \(a\) 的每个元素都是原先 \(a\) 的某个元素。若能快速求得变换 \(k\) 次后的 \(a\) 的第 \(i\) 位是原先 \(a\) 的第几位,则本题也迎刃而解。

  • 一次后,第 \(i\) 位是原先的第 \(x_i\) 位。
  • 两次后,第 \(i\) 位是原先的第 \(x_{x_i}\) 位。
  • ……

\(1 \sim n\) 看作一张图中的结点,以每个 \(i\) 向对应的 \(x_i\) 连一条有向边,每个点恰好有一条出边。

可以看出,设从点 \(i\) 出发沿着出边走 \(k\) 次后到达点 \(p\),则最后的第 \(i\) 位就是原先的第 \(p\) 位。

一张每个点恰好有一条出边的有向图是一片基环内向森林。至此本题被转化为基环内向森林上的“\(k\) 级祖先”问题。

可以通过倍增解决 \(k\) 级祖先问题:设 \(g(j, i)\) 表示从点 \(i\) 出发沿着出边走 \(2^j\) 次后到达的点,则有边界 \(g(0, i) = x_i\) 和转移 \(g(j + 1, i) = g(j, g(j, i))\)。要回答询问,只需将 \(k\) 进行二进制拆分,后对每个进制位 \(j\) 进行 \(p \gets g(j, p)\) 的跳跃即可。

时空复杂度为 \(\mathcal O(n \log k)\)

注:由于这里的 \(k\) 级祖先可以离线询问,存在线性时空的做法。具体而言,找到每个连通块的环后进行 DFS,记录 DFS 栈,然后定位栈上从当前点往前数 \(k\) 位的点,或栈长不足 \(k\) 时定位环上的特定点。由于代码实现比倍增的做法复杂,不展示具体代码。

F - Rearrange Query

题意简述(*1540

给定两个长度为 \(n\) 的序列 \((a_1, \ldots, a_n)\)\((b_1, \ldots, b_n)\)

回答 \(q\) 次询问,每次给定 \(l, r, L, R\)(保证 \(1 \le l \le r \le n\)\(1 \le L \le R \le n\)),判断是否可以通过重排将 \(a\) 的子串 \((a_l, \ldots, a_r)\) 变为 \(b\) 的子串 \((b_L, \ldots, b_R)\)

数据范围:\(n, q \le 2 \times 10^5\)\(1 \le a_i, b_i \le n\)

AC 代码
#include <iostream>
#include <vector>
#include <random>
using std::cin, std::cout;

#define F(i, a, b) for(int i = a; i <= (b); ++i)

void Solve();
int main() {
    cin.sync_with_stdio(false);
    cin.tie(nullptr);
    Solve();
    return 0;
}

using ULL = unsigned long long;

void Solve() {
    int n, q;
    cin >> n >> q;
    std::vector<int> a(n + 1), b(n + 1);
    F(i, 1, n)
        cin >> a[i];
    F(i, 1, n)
        cin >> b[i];
    std::mt19937_64 rng(0x12345);
    std::vector<ULL> g(n + 1);
    F(i, 1, n)
        g[i] = rng();
    std::vector<ULL> sa(n + 1), sb(n + 1);
    sa[0] = sb[0] = 0;
    F(i, 1, n) {
        sa[i] = sa[i - 1] + g[a[i]];
        sb[i] = sb[i - 1] + g[b[i]];
    }
    F(q_, 1, q) {
        int l, r, L, R;
        cin >> l >> r >> L >> R;
        ULL v = sa[r] - sa[l - 1] - sb[R] + sb[L - 1];
        cout << (v ? "No" : "Yes") << '\n';
    }
}

也就是判断子串 \(a[l:r]\)\(b[L:R]\) 作为多重集是否相等。

一个熟知的判断多重集相等的办法是散列。即将多重集映射为一个数,以数的相等多重集的相等,这个数称作散列值。为了确保正确性,需要在映射过程中引入随机性,并通过分析证明错误概率极小。

适用于本题的散列方式是令多重集 \(S = \{ s_1, \ldots, s_k \}\) 映射为 \(\bigl( \sum_{i = 1}^{k} g(s_i) \bigr) \bmod m\)。其中 \(g\) 为一个定义域为 \(s\) 的取值范围,值域为 \([0, m)\) 的均匀随机函数,即所有 \(g(s)\) 对于不同的 \(s\) 来说都是独立的随机变量。

分析:

  • 如果两多重集 \(S, T\) 相同,则它们通过散列被映射到的数也相同。
  • 如果两多重集 \(S, T\) 不同,则它们的对称差 \(D = S \mathbin{\Delta} T\) 非空。
    • 同时,作差得到 \(S' = S \setminus T\)\(T' = T \setminus S\),此时有 \(S'\)\(T'\) 无交且 \(D = S' \cup T'\)
    • 此时 \(S, T\) 的散列值相同当且仅当 \(S', T'\) 的散列值相同。
    • \(S'\) 中有 \(k\) 种数值 \((s_1, \ldots, s_k)\) 分别出现了 \((c_1, \ldots, c_k)\) 次;
    • \(T'\) 中有 \(l\) 种数值 \((t_1, \ldots, t_l)\) 分别出现了 \((d_1, \ldots, d_l)\) 次;
    • \(k + l \ge 1\),即 \(S' \cup T' = D\) 非空。
    • \(S', T'\) 的散列值相同等价于 \(\sum_{i = 1}^{k} c_i \cdot g(s_i) + \sum_{i = 1}^{l} (-d_i) \cdot g(t_i) \equiv 0 \pmod{m}\)
    • 根据 \(k + l \ge 1\),求和式中至少包含一项,设为 \(C \cdot g(V)\),即该项的 \(c_i\)\(-d_i\) 记作 \(C\)\(s_i\)\(t_i\) 记作 \(V\)
    • \(g(V)\) 是在 \([0, m)\) 中均匀取值的随机变量,则(根据熟知数论知识)该项取任意特定值的概率最大不会超过 \(\gcd(C, m) / m\)
    • \(S, T\) 的散列值相同的概率不超过 \(\gcd(C, m) / m\),其中 \(C\) 可以在 \(\lvert C \rvert \le n\)\(C \ne 0\) 的条件下取“最坏”的值。

其中 \(C\) 最坏的值可以取到 \(n\) 以内 \(m\) 的最大因数。

一种最优的选择是取 \(\bm{m = p}\) 为素数,从而只要 \(n < p\),错误率就不超过 \(1 / p\)

回看散列函数 \(S \mapsto \bigl( \sum_{i = 1}^{k} g(s_i) \bigr) \bmod m\),当作用于序列 \(a, b\) 的区间上时,可以用前缀和的手法处理:令 \(\Sigma a_i = \bigl( \sum_{j = 1}^{i} g(a_j) \bigr) \bmod m\),则 \(a[l:r]\) 作为多重集的散列值为 \((\Sigma a_r - \Sigma a_{l - 1}) \bmod m\)。对序列 \(b\) 的处理同理。

代码实现上,可以取 \(m = 2^{64}\),使用 unsigned long long 进行计算,并使用 C++ 中的伪随机数生成器 std::mt19937_64 来生成 \(g\) 的取值。

  • 此时单次错误率不超过 \(n / m\),故总错误率不超过 \(q n / m\)。取 \(m = \Omega(\epsilon^{-1} q n)\) 即可达到 \(\mathcal O(\epsilon)\) 的错误率要求。
  • 计算可知,在 \(m = 2^{64}\) 的设置下,单测试点错误率不超过 \(3 \times 10^{-9}\)

时间复杂度为 \(\mathcal O(n + q)\)

注:存在其他多种不同的散列方式。

G - Sum of (XOR^K or 0)

题意简述(*3110

给定长度为 \(n\) 的值域在 \([0, 2^b)\) 内的非负整数序列 \((a_1, \ldots, a_n)\) 和正整数 \(m, k\)。求

\[\left( \sum_{\substack{S \subseteq \{ 1, \ldots, n \} \\ \lvert S \rvert \equiv 0 \pmod{m}}} [S \ne \varnothing] \Biggl( \bigoplus_{i \in S} a_i \Biggr)^k \right) \bmod 998244353 \text{。} \]

数据范围:\(n \le 2 \times 10^5\)\(a_i < 2^b\)\(b \le 20\)\(m \le 100\)\(1 \le k \le 2 \times 10^5\)

AC 代码

代码中逆 Walsh 变换时没有除以 \(2^b\),改为在输出答案前除。

#include <iostream>
#include <vector>
using std::cin, std::cout;

#define F(i, a, b) for(int i = a; i <= (b); ++i)
#define F2(i, a, b) for(int i = a; i < (b); ++i)

void Solve();
int main() {
    cin.sync_with_stdio(false);
    cin.tie(nullptr);
    Solve();
    return 0;
}

using LL = long long;
const int Mod = (119 << 23) + 1;
const int B = 20, V = 1 << B, iV = Mod - ((Mod - 1) >> B);

int qPow(int b, int e) {
    int a = 1;
    for (; e; e >>= 1, b = (int)((LL)b * b % Mod))
        if (e & 1)
            a = (int)((LL)a * b % Mod);
    return a;
}

void Solve() {
    int n, m, k;
    cin >> n >> m >> k;
    std::vector<int> v(n + 1, 0); /* to build v */ {
        std::vector<std::vector<int>> plus(n + 1, std::vector<int>(m, 0));
        auto minus = plus;
        plus[0][0] = minus[0][0] = 1;
        F(i, 1, n)
            F2(j, 0, m) {
                int j2 = j ? j - 1 : m - 1;
                plus[i][j] = (plus[i - 1][j] + plus[i - 1][j2]) % Mod;
                minus[i][j] = (minus[i - 1][j] - minus[i - 1][j2] + Mod) % Mod;
            }
        F(i, 0, n)
            F2(j, 0, m)
                v[i] = (int)((v[i] + (LL)plus[i][j] * minus[n - i][j ? m - j : 0]) % Mod);
    }
    std::vector<int> f(V, 0);
    F(i, 1, n) {
        int a;
        cin >> a;
        ++f[a];
    }
    F2(j, 0, B)
        F2(i, 0, V)
            if (~i >> j & 1) {
                int x = f[i];
                int& y = f[i | 1 << j];
                f[i] = x + y;
                y = x - y;
            }
    F2(i, 0, V)
        f[i] = v[(f[i] + n) / 2];
    F2(j, 0, B)
        F2(i, 0, V)
            if (~i >> j & 1) {
                int x = f[i];
                int& y = f[i | 1 << j];
                f[i] = (x + y) % Mod;
                y = (x - y + Mod) % Mod;
            }
    int ans = 0;
    F2(i, 0, V)
        if (f[i])
            ans = (int)((ans + (LL)f[i] * qPow(i, k)) % Mod);
    ans = (int)((LL)ans * iV % Mod);
    cout << ans << '\n';
}

由于 \(b \le 20\),考虑 \(\tilde{\mathcal O}(2^b)\) 的做法是自然的。

如果可以对于每个值 \(v \in [0, 2^b)\) 求出有多少个满足限制的 \(S\) 使得 \(\displaystyle \bigoplus_{i \in S} a_i\) 就等于 \(v\),不妨记这样的 \(S\) 的数量为 \(f_v\),则答案为 \(\displaystyle \Biggl( \sum_{v = 0}^{2^b - 1} (f_v - [v = 0]) \cdot v^k \Biggr) \bmod 998244353\)
(减去 \([v = 0]\) 是因为要扣除 \(S\) 为空集的情况,其恰好对应 \(v = 0\)。不过本题保证 \(k \ge 1\),因此 \(v = 0\)\(v^k = 0\),所以不管也行。)

只需求出所有 \(f_v \bmod 998244353\) 即可。本题没有需要讨论数论性质的部分(除了要除以 \(2^b\)),故后文中省略对模 \(998244353\) 的讨论。

\(\displaystyle f_v = [x^v y^0] \prod_{i = 1}^{n} (1 + x^{a_i} y)\),其中元 \(x\) 进行异或卷积,元 \(y\) 进行长度为 \(\bm m\) 的循环卷积

可以对 \(x\) 的维度进行 Walsh 变换将异或卷积转换为点积,但由于缺乏 \(m\) 次单位根 \(\omega_m\),保留 \(y\) 的维度进行普通的循环卷积。这里我们的底气是 \(m \le 100\) 范围不大。

我们知道 \(1\)(即 \(x^0\),仅有 \(0\) 处的值非零且为 \(1\) 的序列)的 Walsh 变换为全 \(1\) 序列。设 \(x^a\)(即仅有 \(a\) 处的值非零且为 \(1\) 的序列)的 Walsh 变换为序列 \(({h^a}_w)\),这里每个 \({h^a}_w\) 均为 \(1\)\(-1\)。根据 Walsh 变换的线性性,一个因子 \((1 + x^a y)\) 的 Walsh 变换就为序列 \(({g^a}_w)\),其中 \({g^a}_w = 1 + {h^a}_w y\)

于是将每个因子的序列 \(({g^a}_w)\) 全部点积起来后再取 \(y^0\) 次项,就得到 \((f_v)\) 的 Walsh 变换 \(\mathcal W f\)。可知 \(\displaystyle \mathcal W f_w = [y^0] \prod_{i = 1}^{n} (1 + {h^{a_i}}_w y)\),由于每个 \({h^{a_i}}_w\) 均为 \(1\)\(-1\),简化为 \(\mathcal W f_w = [y^0] (1 + y)^{c_w} (1 - y)^{n - c_w}\),其中 \(c_w\) 为满足 \({h^{a_i}}_w = 1\)\(i\) 的数量。

问题来到如何对每个 \(w\) 计算 \(c_w\),即满足 \({h^{a_i}}_w = 1\)\(i\) 的数量。由于每个 \({h^{a_i}}_w\) 均为 \(1\)\(-1\),有 \(\displaystyle \sum_{i = 1}^{n} {h^{a_i}}_w = c_w \cdot (1) + (n - c_w) \cdot (-1) = 2 c_w - n\),逆用此式即得 \(\displaystyle c_w = \frac{1}{2} \Biggl( n + \sum_{i = 1}^{n} {h^{a_i}}_w \Biggr)\)

根据 Walsh 变换的线性性,既然 \(({h^{a_i}}_w)\)\(x^{a_i}\) 的 Walsh 变换,就有 \(\displaystyle \Biggl( \sum_{i = 1}^{n} {h^{a_i}}_w \Biggr)\)\(\displaystyle \sum_{i = 1}^{n} x^{a_i}\) 的 Walsh 变换。于是只需对 \(\displaystyle \sum_{i = 1}^{n} x^{a_i}\) 进行一次快速 Walsh 变换即可求得所有的 \(c_w\)

接下来解决已知 \(c_w\)\(\mathcal W f_w = [y^0] (1 + y)^{c_w} (1 - y)^{n - c_w}\) 的部分。注意 \(m \le 100\) 的范围使得 \(\mathcal O(n m)\) 的复杂度是可以接受的,可以直接对每个 \(0 \le i \le n\) 预处理出每个 \((1 + y)^i\)\((1 - y)^i\)(注意是循环卷积,所以长度只有 \(m\)),然后就有 \(\displaystyle [y^0] (1 + y)^c (1 - y)^{n - c} = \sum_{j = 0}^{m - 1} \bigl( [y^j] (1 + y)^c \bigr) \cdot \bigl( [y^{m - j}] (1 - y)^{n - c} \bigr)\)。这部分的时空复杂度均为 \(\mathcal O(n m)\)

最后,在求得 \((\mathcal W f_w)\) 后,将 \(\mathcal W f\) 进行一次快速逆 Walsh 变换即得 \((f_v)\),直接计算 \(\displaystyle \sum_{v = 0}^{2^b - 1} f_v \cdot v^k\) 即可。

时间复杂度为 \(\mathcal O(n m + 2^b (b + \log k))\),空间复杂度为 \(\mathcal O(n m + 2^b)\)。其中 \(\log k\) 来自求 \(v^k\) 时使用的快速幂。

注:

后记:我本来想的线性基,在提示做法涉及 FWT 后想出来了,所以总体上算独立做出的。

posted @ 2024-08-19 06:20  粉兔  阅读(602)  评论(1编辑  收藏  举报