直接DP不可做(因为按位取或不满足最优化原理)。
而求最大/小与/或/异或和,可以考虑利用二分的思想,在二进制意义下从高到低确定答案的每一位。在此题中要求最小或和,因此从高往低考虑到每一位时,这一位能为
0
就为
而如何判断能否为
下面定义一个
f[i][j] 表示到第 i 个雕塑,分成
转移即枚举上一段的开头 k ,如果存在一个
1、 f[k][j−1]=true ,即如果到第 k 个雕塑,分成
2、设 sum[] 为雕塑年龄的前缀和,条件为 sum[i]−sum[k] 的第 x 位为
3、对于任意的 h>x ,如果 ans 的第 h 位为
or(sum[i]−sum[k],ans,x)=ans
满足这 3 个条件则
如果最后存在一个 A≤i≤B 使得 f[n][i]=true ,则第 x 为定为
时间复杂度为 O(n3log∑ni=1Yi) ,无法通过 2000 的数据点。
但是 2000 的数据点的特性是 A=1 ,因此设 g[i] 表示到了第 i 位,满足条件并且第
这时候转移条件和 f 基本相似,即枚举上一段的开头
最后如果 g[n]≤B ,那么第 x 位定为
因此 A=1 时复杂度 O(n2log∑ni=1Yi) 。
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
typedef long long ll;
const int N = 2005, INF = 0x3f3f3f3f;
int n, A, B, a[N], g[N]; bool f[N][N];
ll sum[N], ans;
ll Or(ll x, ll y, int k) {
x >>= k; y >>= k; return (x | y) << k;
}
bool _(int x) {
int i, j, k; for (i = 0; i <= n; i++) for (j = 0; j <= n; j++)
f[i][j] = 0; f[0][0] = 1;
for (j = 1; j <= B; j++) for (i = 1; i <= n; i++)
for (k = j - 1; k < i; k++) if (f[k][j - 1] &&
Or(sum[i] - sum[k], ans, x) == ans)
f[i][j] = 1;
for (i = A; i <= B; i++) if (f[n][i]) return 1;
return 0;
}
bool __(int x) {
int i, j; memset(g, INF, sizeof(g)); g[0] = 0;
for (i = 1; i <= n; i++) for (j = 0; j < i; j++)
if (Or(sum[i] - sum[j], ans, x) == ans)
g[i] = min(g[i], g[j] + 1);
return g[n] <= B;
}
bool Try(int x) {
if (A == 1) return __(x);
return _(x);
}
int main() {
int i; n = read(); A = read(); B = read();
for (i = 1; i <= n; i++) sum[i] = sum[i - 1] + (a[i] = read());
for (i = 43; i >= 0; i--) if (!Try(i)) ans |= 1ll << i;
cout << ans << endl;
return 0;
}