Luogu P14032 【MX-X20-T6】「FAOI-R7」超级电话
cnblogs。
再遇百万富翁,结果没意识到询问写的是
首先考虑
因为
那就相当于是在
能够知道在全局异或的值都为
那如果此时全局异或的值
于是建立 trie,维护每个节点的子树信息即可,
似乎后面的表述会有些歧义,这里认为 trie 树更低的层指的是是子树大小越小,该层节点数越多的层。
接下来考虑后面的问题:
此时能够发现 trie 的层数是硬伤,但是为了满足
具体来说,考虑从高到低每一块分别有
这样来说就保证了查询时可以每一块至多查询一片,不过这一片该怎么处理呢?
这相当于是对于每个第
看起来似乎所需要的信息数特别多,不过考虑实际分析后(从高到低分析左右子树选取情况)发现信息数仅为
那么对于
于是设计一个 dp,
复杂度为
如果要看这个的代码放在后面了,输出方案可以得到:
- 当
B = 5 时,c_{1\sim 5} = [9, 5, 3, 2, 1] 。 - 当
B = 6 时,c_{1\sim 6} = [8, 5, 3, 2, 1, 1] 。 - 当
B = 7 时,c_{1\sim 7} = [8, 4, 3, 2, 1, 1, 1] 。
对于实现的部分,应当有很多种方法,我觉得我的写法很逆天,建议不要参考。
对于预处理,我因为不知道怎么表示具体的
对应的处理顺序就是从低到高做,因为高层的节点需要低层的信息。
在处理块内 trie 的信息时,我是从低到高每次合并相邻的两个子树,具体来说就是维护每个子树的
那么可以知道
重复这个操作
对于询问,如果暴力就是
又因为我的做法不能很好的定位,我的做法是首先从高到低定位出每一块,然后在第
这里的暴力分解指的是,直接求出
两部分的复杂度都不能很好表示出来,能过就对了。
输出方案(这里输出的是压缩 trie 树从低到高的
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("");
}
通过代码:
#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;
}