BZOJ 4069 - [Apio2015]巴厘岛的雕塑
Published on 2016-05-04题目描述
印尼巴厘岛的公路上有许多的雕塑,我们来关注它的一条主干道。
在这条主干道上一共有 座雕塑,为方便起见,我们把这些雕塑从 到 连续地进行标号,其中第 座雕塑的年龄是 年。为了使这条路的环境更加优美,政府想把这些雕塑分成若干组,并通过在组与组之间种上一些树,来吸引更多的游客来巴厘岛。
下面是将雕塑分组的规则:
这些雕塑必须被分为恰好 组,其中 ,每组必须含有至少一个雕塑,每个雕塑也必须属于且只属于一个组。同一组中的所有雕塑必须位于这条路的连续一段上。
当雕塑被分好组后,对于每个组,我们首先计算出该组所有雕塑的年龄和。
计算所有年龄和按位取或的结果。我们这个值把称为这一分组的最终优美度。
请问政府能得到的最小的最终优美度是多少?
分析
首先要知道,像状态 f[i][j] 将前 i 个分为 j 组得到的最小优美值
是不行的,原因是这里不是加法,而是按位或,动态规划的最优子结构贪心在这里不一定成立,所以这个 的暴力行不通。
有位运算?不妨考虑从高位向低位枚举,如果最高位可以是 ,那么就让它为 ,原因是如果最高为为 的话,后面的位即使全部是 0 也无法弥补,这是一个常用的枚举技巧。
枚举当前位 ,定义状态 将前 个分为 组得到能否在满足前面位成立的情况下,使得当前位 为 。
怎么才算满足前面位成立呢 ?假如上一位枚举的结果是可以为 ,那么这次转移的时候每组和的对应二进制位就只能为 ;假如上一位枚举的结果是为 ,那么每组和的对应二进制位就是随意。这里采用按位或来巧妙判断,参见代码。
则当前 位可为 0 当且仅当 。
这一算法的复杂度是: 。不足以通过最后一个子任务。
由于最后一个子任务的 ,考虑精简状态。 使得前 位在满足前面位成立的情况下,使得当前位 为 至少要分多少组。则当前 位可为 0 当且仅当 。
复杂度 。
代码
// Created by Sengxian on 5/3/16. // Copyright (c) 2016年 Sengxian. All rights reserved. // BZOJ 4069 DP + 位运算 #include <algorithm> #include <iostream> #include <cctype> #include <cstring> #include <cstdio> #include <cmath> #include <vector> #include <queue> using namespace std; inline int ReadInt() { static int n, ch; n = 0, ch = getchar(); while (!isdigit(ch)) ch = getchar(); while (isdigit(ch)) n = (n << 3) + (n << 1) + ch - '0', ch = getchar(); return n; } typedef long long ll; const int maxn = 2000 + 3; int n, A, B, g[maxn]; bool f[maxn][maxn]; ll s[maxn]; inline bool check(ll sum, int bit, ll ans) { //ans 为 1 的部分,sum 为 0 也是合法的,这里采用按位或来巧妙判断 return ((sum >> (bit + 1)) | ans) == ans && ((sum >> bit) & 1) == 0; } ll solve2() { ll ans = 0; for (int bit = (int)log2(s[n]); bit >= 0; --bit) { g[0] = 0; for (int i = 1; i <= n; ++i) { g[i] = B + 1; for (int k = 1; k <= i; ++k) if (g[k - 1] + 1 < g[i] && check(s[i] - s[k - 1], bit, ans)) //g[k - 1] + 1 < g[i] 这个剪枝直接快了4s g[i] = min(g[i], g[k - 1] + 1); } ans = ans << 1 | (g[n] > B); } return ans; } ll solve1() { ll ans = 0; for (int bit = (int)log2(s[n]); bit >= 0; --bit) { memset(f[0], 0, sizeof f[0]); f[0][0] = true; for (int i = 1; i <= n; ++i) for (int j = 1; j <= min(i, B); ++j) { f[i][j] = false; for (int k = 1; k <= i && !f[i][j]; ++k) f[i][j] |= f[k - 1][j - 1] && check(s[i] - s[k - 1], bit, ans); } bool flag = false; for (int i = A; i <= B && !flag; ++i) flag |= f[n][i]; ans = ans << 1 | !flag; } return ans; } int main() { #ifndef ONLINE_JUDGE freopen("test.in", "r", stdin); #endif n = ReadInt(), A = ReadInt(), B = ReadInt(); for (int i = 1; i <= n; ++i) s[i] = s[i - 1] + ReadInt(); printf("%lld\n", A > 1 ? solve1() : solve2()); return 0; }