BZOJ4069【APIO2015】巴厘岛的雕塑 < DP >

Problem

【APIO2015】巴厘岛的雕塑

Time  Limit:  10  Sec\mathrm{Time\;Limit: \;10\;Sec}
Memory  Limit:  64  MB\mathrm{Memory\;Limit:\;64\;MB}

Description

印尼巴厘岛的公路上有许多的雕塑,我们来关注它的一条主干道。
在这条主干道上一共有NN座雕塑,为方便起见,我们把这些雕塑从11NN连续地进行标号,其中第 i 座雕塑的年龄是YiY_i年。为了使这条路的环境更加优美,政府想把这些雕塑分成若干组,并通过在组与组之间种上一些树,来吸引更多的游客来巴厘岛。
下面是将雕塑分组的规则:
这些雕塑必须被分为恰好XX组,其中AXBA\le X\le B,每组必须含有至少一个雕塑,每个雕塑也必须属于且只属于一个组。同一组中的所有雕塑必须位于这条路的连续一段上。
当雕塑被分好组后,对于每个组,我们首先计算出该组所有雕塑的年龄和。
计算所有年龄和按位取或的结果。我们这个值把称为这一分组的最终优美度。
请问政府能得到的最小的最终优美度是多少?

Input

输入的第一行包含三个用空格分开的整数N,A,BN,A,B
第二行包含NN个用空格分开的整数Y1,Y2,,YNY_1,Y_2,\cdots,Y_N

Output

输出一行一个数,表示最小的最终优美度。

Sample Input

1
2
6 1 3
8 1 2 1 5 4

Sample Output

1
11

Explanation

将这些雕塑分为22组,(8,1,2)(8,1,2)(1,5,4)(1,5,4),它们的和是(11)(11)(10)(10),最终优美度是(1110)=11(11\mid10)=11。不难验证,这也是最终优美度的最小值。

HINT

子任务编号 NN A,BA,B YiY_i 分值
11 1N201\le N\le20 A,BNA,B\le N 0Yi1090\le Y_i\le10^9 9  pts\mathrm{9\;pts}
22 1N501\le N\le 50 A,Bmin(20,N)A,B\le\min(20,N) 0Yi100\le Y_i\le10 16  pts\mathrm{16\;pts}
33 1N1001\le N\le100 A=1,  BNA=1,\;B\le N 0Yi200\le Y_i\le20 21  pts\mathrm{21\;pts}
44 1N1001\le N\le100 A,BNA,B\le N 0Yi1090\le Y_i\le10^9 25  pts\mathrm{25\;pts}
55 1N20001\le N\le2000 A=1,  BNA=1,\;B\le N 0Yi1090\le Y_i\le10^9 29  pts\mathrm{29\;pts}

标签:DP

Solution

奇葩的面向数据编程题。

由于算答案是按位或,可以考虑从高位向低位贪心,每次判断在前面的位都取到最优情况下,这一位能否取00。这个判断的过程可以DP\mathrm{DP}实现。
f[i][j]f[i][j]表示考虑前ii个雕塑,分成jj个组,能否在当前位上取00
那么f[i][j]=truef[i][j]=true当且仅当k[j1,i)\exists k\in[j-1,i),满足f[k][j1]=truef[k][j-1]=true,并且sumk+1,isum_{k+1,i}和当前答案取或的结果还是当前答案(即不会使得前面位上不为最优解),还需要sumk+1,jsum_{k+1,j}在当前位上的值为00(这样当前位才能取00)。
求出所有ff后,判断是否t[A,B]\exists t\in[A,B]满足f[n][t]=truef[n][t]=true,如果存在则可以取00,否则此位取11

然而这样并不能得满分。上面的方法是O(n3)O(n^3)处理出所有ff值,不能通过n=2000n=2000Subtask  5\mathrm{Subtask\;5}

而对于Subtask  5\mathrm{Subtask\;5},发现A=1A=1,则每次使得分的组数量尽量小肯定是最优的。于是用g[i]g[i]表示当前位考虑前ii个雕塑,最少需要分成几组才能使当前位上可以取00。如果在当前位处理gg后发现g[n]>Bg[n]>B,那么必须取11,否则可以取00。这样复杂度降成了O(n2)O(n^2),可以解决Subtask  5\mathrm{Subtask\;5}

综上,特判数据分两种情况分别做即可。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <bits/stdc++.h>
#define MAX_N 2000
using namespace std;
typedef long long lnt;
template <class T> inline void read(T &x) {
x = 0; int c = getchar(), f = 1;
for (; !isdigit(c); c = getchar()) if (c == 45) f = -1;
for (; isdigit(c); c = getchar()) (x *= 10) += f*(c-'0');
}
int n, m, A, B; lnt s[MAX_N+5], ans;
bool f[MAX_N+5][MAX_N+5]; int g[MAX_N+5];
void sub1() {
for (int p = m, flag = 1; p; p--, flag = 1) {
memset(f, false, sizeof f), f[0][0] = true;
for (int i = 1; i <= n; i++) for (int j = 1; j <= i; j++)
for (int k = j-1; k < i; k++) if (f[k][j-1]) {
if ((((s[i]-s[k])>>p)|ans)^ans) continue;
if ((s[i]-s[k])&(1LL<<(p-1))) continue;
f[i][j] = true; break;
}
for (int i = A; i <= B; i++)
if (f[n][i]) {flag = 0; break;}
(ans <<= 1) |= flag;
}
printf("%lld\n", ans);
}
void sub2() {
for (int p = m; p; p--) {
memset(g, 0x3f, sizeof g), g[0] = 0;
for (int i = 1; i <= n; i++) for (int k = 0; k < i; k++) {
if ((((s[i]-s[k])>>p)|ans)^ans) continue;
if ((s[i]-s[k])&(1LL<<(p-1))) continue;
g[i] = min(g[i], g[k]+1);
}
(ans <<= 1) |= (g[n] > B);
}
printf("%lld\n", ans);
}
int main() {
read(n), read(A), read(B);
for (int i = 1; i <= n; i++)
read(s[i]), s[i] += s[i-1];
for (lnt i = s[n]; i; i >>= 1) m++;
return (A != 1 ? sub1() : sub2()), 0;
}