一、题目
1、题目描述
2、输入输出
2.1输入
2.2输出
3、原题链接
1863F - Divide, XOR, and Conquer
二、解题报告
1、思路分析
先思考暴力区间dp:
f(l, r) 代表 区间[l, r] 是否可以保留
N^2 状态 O(N)转移,时间复杂度为O(N^3)会爆掉
考虑优化
由于我们最终关注的是f(i, i),所以区间dp应该是由长区间转移到短区间
对于f(l, r) 如果可以 由 f(l, R)转移,其中 R >= r
或者可以由 f(L, r) 转移,其中L <= l
我们看xor(l, r) 和 xor(L, r) 和 xor(l, R) 之间有何特点,从而寻找优化之处
我们以(l, r) 和 (l, R)为例
如果 (l, r)保留,则 xor(l, r) >= xor(l, R) ^ xor(l, r)
我们拆分二进制位从高到低来看,
对于xor(l, R) 是 0 的位,xor(l, r)是1是0无所谓
对于xor(l, R) 是 1 的位,xor(l, r)是1则会使得xor(l, R) ^ xor(l, r) < xor(l, r)
是0,则没用
于是,转移条件变为 xor(l, r) & xor(l, R) 是否大于0
具体代码中,我们由于自上而下的转移,所以我们开两个数组
L[i], R[i],分别代表左端点为 i 的区间为1的位集合,右端点为 i 的区间为1的位集合
然后对于异或值为0我们特判一下即可
2、复杂度
时间复杂度: O(N^2)空间复杂度:O(N)
3、代码详解
#include <bits/stdc++.h>
#include <ranges>
#define sc scanf
using i64 = long long;
using i128 = __int128;
using PII = std::pair<int, int>;
constexpr int inf32 = 1e9 + 7;
constexpr i64 inf64 = 1e18 + 7;
constexpr int P = 1e9 + 7;
constexpr double eps = 1e-6;
// #define DEBUG
void solve()
{
int n;
std::cin >> n;
std::vector<i64> a(n), acc(n + 1);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
acc[i + 1] = acc[i] ^ a[i];
}
std::vector<i64> L(n + 1), R(n + 1);
auto hi = [&](i64 x) -> i64 {
return (x != (1LL << 61)) ? (1LL << (63 - __builtin_clzll(x))) : (1LL << 61);
};
auto sum = [&](int l, int r) -> i64 {
return (acc[r] ^ acc[l - 1]) ? (acc[r] ^ acc[l - 1]) : (1LL << 61);
};
std::vector<int> ans(n + 1);
L[1] = hi(sum(1, n)), R[n] = hi(sum(1, n)), ans[1] = 1;
for (int len = n - 1; len; -- len)
{
for (int i = 1; i + len <= n + 1; ++ i)
{
int j = i + len - 1;
i64 f = (((sum(i, j) | (1LL << 61)) & L[i]) | ((sum(i, j) | (1LL << 61)) & R[j]));
if (f) L[i] |= hi(sum(i, j)), R[j] |= hi(sum(i, j));
if (len == 1) ans[i] = (f > 0);
}
}
for (int i = 0; i < n; ++ i)
std::cout << ans[i + 1];
std::cout << "\n";
}
int main()
{
#ifdef DEBUG
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(false), std::cin.tie(nullptr), std::cout.tie(nullptr);
int _ = 1;
std::cin >> _;
while (_--)
solve();
return 0;
}