题目描述
难度分:2600
输入T(≤104)表示T组数据。所有数据的n之和≤104。
每组数据输入n(1≤n≤104)和长为n的数组a(0≤a[i]<260)。
不断执行如下操作,直到a中只有一个元素:把a分成左右两段,丢弃其中异或和小的那段。如果两段异或和相同,丢弃哪个都可以。
对于每个a[i],回答:最终剩下的数能否是a[i]?输出一个长为n的01
字符串,其中第i个字符表示a[i]的答案(0表示不能,1表示能)。
输入样例
6
6
3 2 1 3 7 4
5
1 1 1 1 1
10
1 2 4 8 4 1 2 3 4 5
5
0 0 0 0 0
5
1 2 3 0 1
1
100500
输出样例
111111
10101
0001000000
11111
11001
1
算法
动态规划
状态定义
看着像是区间DP
,f[i][j]表示子数组[i,j]是否能够保留下来。在这个定义下,答案就应该是f[i][j](i∈[1,n])。
状态转移
从大区间转移到小区间,对于某个区间[l,r],考虑把[l,r]分成两段的分割点k,它能将[l,r]分割为[l,k]和(k,r]两个子数组。预处理出一个前缀异或和数组s,枚举k的时候比较这两段的区间异或和,判断哪一段可以留下来。
但是这样经典的区间DP
状态转移是O(n)的,整体的时间复杂度通常都为O(n3),在本题n≤104的数据规模下根本行不通。我们考虑如何优化转移,设sl,r为子数组[l,r]的异或和。考虑区间[l,k]什么时候可以从区间[l,r]转移过来(l≤k<r),分为两种情况:
- 如果sl,r的第x位为0,不论sl,k这一位取什么值都会与另外一半相等(如果是1,另一半这一位也必须是1才能使得sl,r这一位是0;如果是0,另一半这一位也必须是0才能使得sl,r这一位是0)。
- 如果sl,r这一位是1,必须在这是最高位的情况下才要求sl,k这一位也是1。不然因为sl,k前面已经比另一半大了,这一位取值将不再有限制。
因此,只有sl,k在highbit(sl,r)位上是1时(highbit(x)表示x最高位的1,与lowbit相对应),[l,r]才能成为[l,k]的转移来源。
最后就是老套路了,外层倒序(先枚举大区间再枚举小区间)循环枚举区间长度len,然后内层循环枚举区间左端点l,右端点为r=l+len−1。由sl,r=sl,k⊕sk+1,r,易知highbit(sl,r)=max(highbit(sl,k),higbit(sk+1,r))。
如果[l,k]可以被保留,只能是highbit(sl,k)=higbit(sl,r)。L[i]表示以i为左端点的所有区间异或值的最高位,每遇到一个新的可行区间[i,j],就把highbit(si,j)异或到L[i]上。同理,R[j]表示以j为右端点的所有区间异或值的最高位,每遇到一个新的可行区间[i,j],就把highbit(si,j)异或到R[j]上。这样的话,每次只要L[i]∧si,j或者R[j]∧si,j不为0,就说明区间[i,j]是个可行区间。这是因为区间长度是从大到小枚举的,此时L和R中存储的是比[i,j]更大的区间信息,说明在某个端点重合的大区间中,可以保留住区间[i,j]。
复杂度分析
时间复杂度
状态数量是O(n2)级别(但是并不需要把dp数组f的空间开出来),单次转移是O(1),因此整个算法的时间复杂度为O(n2)。
空间复杂度
前缀异或和数组s的空间为O(n);L数组和R数组的空间也是O(n);而由于不是有顺序的线性DP
,需要构建一个答案数组,空间也是O(n)的。因此,整个算法的额外空间复杂度为O(n)。
C++ 代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e4 + 5;
int T, n;
LL a[N], s[N], L[N], R[N], ans[N];
// 最高位的1,对应lowbit
LL highbit(LL x) {
return x != (1ll<<62)? (1ull<<(63 - __builtin_clzll(x))): 1ll<<62;
}
// 区间异或和
LL sum(int l, int r) {
return (s[r]^s[l - 1])? (s[r]^s[l - 1]): 1ll<<62;
}
signed main(){
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
L[i] = R[i] = 0;
}
for(int i = 1; i <= n; i++) {
s[i] = s[i - 1] ^ a[i];
}
L[1] |= highbit(sum(1, n));
R[n] |= highbit(sum(1, n));
ans[1] = 1;
for(int len = n - 1; len >= 1; len--) {
for(int i = 1; i <= n - len + 1; i++) {
int j = i + len - 1;
LL flag = (((sum(i, j)|(1ll<<62))&L[i])|((sum(i, j)|(1ll<<62))&R[j]));
if(flag) {
// [i,j]是可保留的区间
L[i] |= highbit(sum(i, j));
R[j] |= highbit(sum(i, j));
}
if(len == 1) {
ans[i] = flag > 0; // 区间长度为1时计算答案
}
}
}
for(int i = 1; i <= n; i++) {
printf("%lld", ans[i]);
}
puts("");
}
return 0;
}