专栏文章
Luogu P14032 【MX-X20-T6】「FAOI-R7」超级电话
P14032题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @minwg1ql
- 此快照首次捕获于
- 2025/12/02 09:28 3 个月前
- 此快照最后确认于
- 2025/12/02 09:28 3 个月前
再遇百万富翁,结果没意识到询问写的是 的光荣倒闭了。
首先考虑 该如何处理这个问题。
因为 ,又因为操作是基于二进制,所以可以想到分开每一位去看待。
因为 ,又因为操作是基于二进制,所以可以想到分开每一位去看待。
那就相当于是在 的 01trie 上做这个问题。
能够知道在全局异或的值都为 (即 均为 )时, 能在 trie 上被拆成不超过 个满子树。
那如果此时全局异或的值 不为 呢?能发现也是不超过 个满子树, 的第 位只相当于影响了第 层是先取左儿子还是右儿子。
能够知道在全局异或的值都为 (即 均为 )时, 能在 trie 上被拆成不超过 个满子树。
那如果此时全局异或的值 不为 呢?能发现也是不超过 个满子树, 的第 位只相当于影响了第 层是先取左儿子还是右儿子。
于是建立 trie,维护每个节点的子树信息即可,。
似乎后面的表述会有些歧义,这里认为 trie 树更低的层指的是是子树大小越小,该层节点数越多的层。
接下来考虑后面的问题:。
此时能够发现 trie 的层数是硬伤,但是为了满足 的条件,且依然要基于二进制处理这个问题,我们考虑对 trie 的层进行分块,得到一个压缩后的 trie 树。
此时能够发现 trie 的层数是硬伤,但是为了满足 的条件,且依然要基于二进制处理这个问题,我们考虑对 trie 的层进行分块,得到一个压缩后的 trie 树。
具体来说,考虑从高到低每一块分别有 层,其中第 块只有 个节点,对于第 块,每个第 块的节点有 个 块的儿子,那第 块就有 个元素。
这样来说就保证了查询时可以每一块至多查询一片,不过这一片该怎么处理呢?
这相当于是对于每个第 块的节点,内部还需要建立起一个 的 01trie,并且对于任意全局异或值和选取的个数都需要保存其信息。
看起来似乎所需要的信息数特别多,不过考虑实际分析后(从高到低分析左右子树选取情况)发现信息数仅为 ,并且因为单独的叶子信息应当是知道的(就是这个节点的各个儿子信息),所以新增的信息数应当为 。
这相当于是对于每个第 块的节点,内部还需要建立起一个 的 01trie,并且对于任意全局异或值和选取的个数都需要保存其信息。
看起来似乎所需要的信息数特别多,不过考虑实际分析后(从高到低分析左右子树选取情况)发现信息数仅为 ,并且因为单独的叶子信息应当是知道的(就是这个节点的各个儿子信息),所以新增的信息数应当为 。
那么对于 ,所需要的总信息数量就为 。
于是设计一个 dp, 表示目前选了 , 的最小代价。
复杂度为 ,在跑出了对应的代价后会发现和题目限制完全一致,那就说明做对了。
复杂度为 ,在跑出了对应的代价后会发现和题目限制完全一致,那就说明做对了。
如果要看这个的代码放在后面了,输出方案可以得到:
- 当 时,。
- 当 时,。
- 当 时,。
对于实现的部分,应当有很多种方法,我觉得我的写法很逆天,建议不要参考。
对于预处理,我因为不知道怎么表示具体的 对应的信息,所以采取的方案是用 xor-hash 后放到哈希表里处理。
对应的处理顺序就是从低到高做,因为高层的节点需要低层的信息。
在处理块内 trie 的信息时,我是从低到高每次合并相邻的两个子树,具体来说就是维护每个子树的 ,其中 是整个子树的信息, 是整个子树在得到 后可能划分出的信息(不包括整个子树的 )。
那么可以知道 合并后得到的就是 。
重复这个操作 次即可。
那么可以知道 合并后得到的就是 。
重复这个操作 次即可。
对于询问,如果暴力就是 复杂度不可接受。
又因为我的做法不能很好的定位,我的做法是首先从高到低定位出每一块,然后在第 块中暴力分解这一块所需的信息,这样复杂度就降到了。
这里的暴力分解指的是,直接求出 的 xor-hash 值再查表。
又因为我的做法不能很好的定位,我的做法是首先从高到低定位出每一块,然后在第 块中暴力分解这一块所需的信息,这样复杂度就降到了。
这里的暴力分解指的是,直接求出 的 xor-hash 值再查表。
两部分的复杂度都不能很好表示出来,能过就对了。
输出方案(这里输出的是压缩 trie 树从低到高的 ):
CPP for (int i = 1; i <= 10; i++) {
val[i] = 1;
for (int j = 1, x = 2; j <= i; j++) {
val[i] += x, x *= 4;
}
val[i] -= (1 << i);
}
memset(dp, 0x3f, sizeof(dp)), dp[0][0] = 0;
for (int i = 0; i <= 20; i++) {
for (int j = 1; i + j <= 20 && j <= 10; j++) {
for (int k = 0; k < 7; k++) {
if (chkmin(dp[i + j][k + 1], dp[i][k] + (1ll << i) * val[j])) {
lst[i + j][k + 1] = j;
}
}
}
}
for (int step : {5, 6, 7}) {
for (int i = 20, j = step; j; j--) {
printf("%d ", lst[i][j]);
i -= lst[i][j];
}
puts("");
}
通过代码:
CPP#include <bits/extc++.h>
using ull = unsigned long long;
constexpr int maxm = 1e7 + 10;
const std::vector<int> way[8] = {
{},
{},
{},
{},
{},
{1, 2, 3, 5, 9},
{1, 1, 2, 3, 5, 8},
{1, 1, 1, 2, 3, 4, 8}
};
int B;
int tot = 1 << 20, totr = 1 << 20;
std::mt19937_64 rand_(std::chrono::steady_clock::now().time_since_epoch().count());
ull rnd[maxm];
__gnu_pbds::cc_hash_table<ull, int> id;
std::vector<int> son[maxm];
void assign(int x, int y, int z);
void init(int n_, int m_, int A_, int B_) {
B = std::min(B_, 7);
for (int i = 0; i < (1 << 20); i++) {
rnd[i] = rand_();
}
auto add = [&](ull x, ull y) {
id[x ^ y] = tot;
assign(id[x], id[y], tot);
tot++;
};
std::vector<std::pair<ull, int>> arr;
for (int i = 0; i < (1 << 20); i++) {
arr.emplace_back(rnd[i], i), id[rnd[i]] = i;
}
for (int d : way[B]) {
std::vector<std::pair<ull, int>> newa;
for (int l = 0; l < arr.size(); l += 1 << d) {
std::vector<std::pair<ull, std::vector<ull>>> group;
for (int i = 0; i < (1 << d); i++) {
son[totr].push_back(arr[l + i].second);
group.push_back({arr[l + i].first, {}});
}
for (int j = 0; j < d; j++) {
std::vector<std::pair<ull, std::vector<ull>>> newg;
for (int i = 0; i < group.size(); i += 2) {
const auto x = group[i];
const auto y = group[i + 1];
std::vector<ull> part{x.first, y.first};
for (auto val : x.second) {
part.push_back(val);
add(val, y.first);
part.push_back(val ^ y.first);
}
for (auto val : y.second) {
part.push_back(val);
add(x.first, val);
part.push_back(x.first ^ val);
}
add(x.first, y.first);
newg.push_back({x.first ^ y.first, part});
}
group = newg;
}
newa.emplace_back(rnd[totr] = group.front().first, totr);
totr++;
}
arr = newa;
}
}
int all = 0;
inline int conv(int x) {
return x < (1 << 20) ? (x ^ all) : x;
}
std::vector<int> query(int x, int y) {
all ^= x, y++;
if (y == (1 << 20)) {
return {tot - 1};
}
std::vector<int> ans;
int sum = 20;
for (int d = B - 1, u = totr - 1; d >= 0; d--) {
int s = way[B][d];
sum -= s;
int xv = (all >> sum) & ((1 << s) - 1);
int yv = (y >> sum) & ((1 << s) - 1);
if (yv) {
ull val = 0;
for (int i = 0; i < yv; i++) {
val ^= rnd[son[u][i ^ xv]];
}
ans.push_back(conv(id[val]));
}
u = son[u][xv ^ yv];
}
return ans;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...