巴厘岛的雕塑 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);
    }
}
posted @ 2017-01-19 23:21  草根柴鸡  阅读(161)  评论(0编辑  收藏  举报