巴厘岛的雕塑 BZOJ 4069
巴厘岛的雕塑
题解:
题意是要求分组使每组的和按位取或的值最小
那么考虑贪心,尽量使高位为0
于是枚举位置,从最高位枚举
假设当前枚举到第l位。
令 f[i][j] 表示前 i 个数分成 j 组,满足前l - 1位的最优答案,当前这一位能否填0
则 f[i][j] = true 当且仅当存在 k 满足 f[k][j - 1] = true 且 (sum[i] - sum[k]) | ans == ans 且 ((sum[i] - sum[k]) >> (l - 1)) & 1 == 0
然后判断f[n][i]中是否有等于true的项,更新临时答案
最后一组数据过大,所以需要特别处理
令g[i]表示前i个数满足临时答案的最少组数
则g[k]能转移到g[i]当且仅当 (((sum[i] - sum[k]) | ans) == ans 且 (((sum[i] - sum[k]) >> (l - 1)) & 1) == 0)
如上更新答案
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
inline int Get()
{
int x = 0;
char c = getchar();
while('0' > c || c > '9') c = getchar();
while('0' <= c && c <= '9')
{
x = (x << 3) + (x << 1) + c - '0';
c = getchar();
}
return x;
}
const int me = 2001;
long long ans;
int len;
int g[me];
long long sum[me];
bool f[101][101];
int n, a, b, c;
int main()
{
n = Get(), a = Get(), b = Get();
for(int i = 1; i <= n; ++i)
{
c = Get();
sum[i] = sum[i - 1] + c;
}
len = log2(sum[n]) + 1;
if(a - 1)
{
for(int l = len; l >= 1; --l)
{
f[0][0] = true;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= i; ++j)
{
f[i][j] = false;
for(int k = 0; k < i; ++k)
{
long long su = sum[i] - sum[k];
if(f[k][j - 1] && ((su >> l) | ans) == ans && ((su >> (l - 1)) & 1ll) == 0)
{
f[i][j] = true;
break;
}
}
}
ans <<= 1;
for(int i = a; i <= b; ++i)
if(f[n][i])
{
ans |= 1;
break;
}
ans ^= 1;
}
printf("%I64d", ans);
}
else
{
for(int l = len; l >= 1; --l)
{
for(int i = 1; i <= n; ++i)
{
g[i] = n + 1;
for(int k = 0; k < i; ++k)
{
long long su = sum[i] - sum[k];
if(((su >> l) | ans) == ans && ((su >> (l - 1)) & 1ll) == 0)
if(g[k] + 1 < g[i]) g[i] = g[k] + 1;
}
}
ans <<= 1;
if(g[n] > b) ans ^= 1;
}
printf("%I64d", ans);
}
}