专栏文章
CF2170F Build XOR on a Segment 题解
题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mimxqnki
- 此快照首次捕获于
- 2025/12/01 17:16 3 个月前
- 此快照最后确认于
- 2025/12/01 17:16 3 个月前
可能第一个想法会是从不同的起点开始 dp,求出 表示从某个起点开始,到右端点 为止,如果要异或出来 ,则至少需要选取几个整数。这个 dp 的转移是很简单的,但是询问个数太多了,即使对序列分块,只取每块的左端点作为起点,询问的复杂度也会爆炸。
那么观察一下,根据线性代数的知识,可以发现每个询问的答案不会超过 ,因为任意 个在 中的整数,一定存在 个整数能被其他 个整数的某个子集的异或表示,于是就可以去掉这个整数。然后就可以想到,从左往右 dp,假设当前右端点为 ,实时维护 表示如果能使用的整数右边到 为止,且希望通过 个整数的异或凑出 ,则左端点的最大值是多少。即 中存在 个整数能异或得出 。这样的话离线查询就很简单了,可以在扫描到右端点 时处理右端点为 的所有询问,找到最小的 使得 即为答案。转移也是简单的,每次遍历整个 数组,然后 。
算一下更新 的复杂度 ,大概是 ,常数很小,一看时间限制 5s,直接冲就完事了。
这场考虑到 dirty 程度 D>>>EF,不好评价。
CPP#include <bits/stdc++.h>
typedef std::pair<int, int> pii;
int a[10005], n, q, f[4096][13], nf[4096][13], ans[1000005], val[1000005];
std::vector<pii> query[10005];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
scanf("%d", &q);
for (int i = 1; i <= q; ++i) {
int l, r;
scanf("%d%d%d", &l, &r, &val[i]);
query[r].push_back(pii(l, i));
}
memset(f, -1, sizeof(f));
for (int i = 1; i <= n; ++i) {
memcpy(nf, f, sizeof(f));
for (int o = 0; o < 4096; ++o) {
nf[a[i]][1] = i;
for (int p = 1; p <= 11; ++p) {
if (f[o][p] == -1) {
continue;
}
nf[o ^ a[i]][p + 1] = std::max(nf[o ^ a[i]][p + 1], f[o][p]);
}
}
memcpy(f, nf, sizeof(f));
for (auto [x, y]: query[i]) {
ans[y] = 0;
for (int p = 1; p <= 12; ++p) {
if (f[val[y]][p] >= x) {
ans[y] = p;
break;
}
}
}
}
for (int i = 1; i <= q; ++i) {
printf("%d ", ans[i]);
}
printf("\n");
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...