巴厘岛的雕塑

Time Limit: 10 Sec Memory Limit: 64 MB

Description

印尼巴厘岛的公路上有许多的雕塑,我们来关注它的一条主干道。

在这条主干道上一共有 N 座雕塑,为方便起见,我们把这些雕塑从 1 到 N 连续地进行标号,其中第 i 座雕塑的年龄是 Yi 年。为了使这条路的环境更加优美,政府想把这些雕塑分成若干组,并通过在组与组之间种上一些树,来吸引更多的游客来巴厘岛。

下面是将雕塑分组的规则:

这些雕塑必须被分为恰好 X 组,其中 A< = X< = B,每组必须含有至少一个雕塑,每个雕塑也必须属于且只属于一个组。同一组中的所有雕塑必须位于这条路的连续一段上。

当雕塑被分好组后,对于每个组,我们首先计算出该组所有雕塑的年龄和。

计算所有年龄和按位取或的结果。我们这个值把称为这一分组的最终优美度。

请问政府能得到的最小的最终优美度是多少?

备注:将两个非负数 P 和 Q 按位取或是这样进行计算的:

首先把 P 和 Q 转换成二进制。

设 nP 是 P 的二进制位数,nQ 是 Q 的二进制位数,M 为 nP 和 nQ 中的最大值。P 的二进制表示为 pM−1pM−2…p1p0,Q 的二进制表示为 qM−1qM−2…q1q0,其中 pi 和 qi 分别是 P 和 Q 二进制表示下的第 i 位,第 M−1 位是数的最高位,第 0 位是数的最低位。

P 与 Q 按位取或后的结果是: (pM−1 OR qM−1)(pM−2 OR qM−2)…(p1 OR q1)(p0 OR q0)。其中:

0 OR 0=0

0 OR 1=1

1 OR 0=1

1 OR 1=1

Input

输入的第一行包含三个用空格分开的整数 N,A,B。

第二行包含 N 个用空格分开的整数 Y1,Y2,…,YN。

Output

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

Sample Input

6 1 3
 8 1 2 1 5 4

Sample Output

11

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

HINT

子任务 1 (9 分)

1< = N< = 20

1< = A< = B< = N

0< = Yi< = 1000000000

子任务 2 (16 分)

1< = N< = 50

1< = A< = B< = min{20,N}

0< = Yi< = 10

子任务 3 (21 分)

1< = N< = 100

A=1

1< = B< = N

0< = Yi< = 20

子任务 4 (25 分)

1< = N< = 100

1< = A< = B< = N

0< = Yi< = 1000000000

子任务 5 (29 分)

1< = N< = 2000

A=1

1< = B< = N

0< = Yi< = 1000000000

Main idea

将一个序列分为若干组,使得每组的和OR起来的值最小。

Solution

根据题意,要使最终的答案最小,可以想到利用贪心,从高到低枚举答案的每一位,如果能取0则取0,否则取1。

问题转化为如何判断答案的某一位能否取0,我们考虑用DP解决这个问题。假设当前枚举到第pos位。

f[i][j]表示前i个数分成j组,显然该位可以填0的条件是:

1.存在k在i前面分了j-1组可行;

2.异或值满足之前已经枚举的相同(保证最小)

3.这一位可以是0

这样可以过71分,最后一组数据TLE,发现最后一组数据下界固定为1,由于显然发现组数越小越优,可以令g[i]表示令第i位为0的最小组数,如果组数<B则这位可以为0。

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#include<bits/stdc++.h>
using namespace std;

const int ONE=2001;

int n,A,B;
int a[ONE],g[ONE];
bool f[ONE][ONE];
long long Sum[ONE];
long long res;
long long total;
int PD,len;

int get()
{
int res,Q=1; char c;
while( (c=getchar())<48 || c>57)
if(c=='-')Q=-1;
if(Q) res=c-48;
while((c=getchar())>=48 && c<=57)
res=res*10+c-48;
return res*Q;
}

void PartOne()
{
for(int pos=len;pos>=1;pos--)
{
memset(f,0,sizeof(f));
f[0][0]=1;

for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
for(int k=0;k<=i-1;k++)
{
total=Sum[i]-Sum[k];

if(f[k][j-1] && ((total>>pos)|res)==res && ((total>>(pos-1)) & (long long)1)==0 )
{
f[i][j]=1;
break;
}
}

PD=0;
for(int i=A;i<=B;i++)
{
PD=f[n][i];
if(PD) break;
}
res<<=1;
if(!PD) res|=1;
}
}

void PartTwo()
{
for(int pos=len;pos>=1;pos--)
{
memset(g,63,sizeof(g));
g[0]=0;
for(int i=1;i<=n;i++)
for(int k=0;k<=i-1;k++)
{
total=Sum[i]-Sum[k];

if(((total>>pos)|res)==res && ((total>>(pos-1)) & (long long)1)==0 )
{
g[i]=min(g[i],g[k]+1);
}
}

res<<=1;
if(g[n]>B) res|=1;
}

}

int main()
{
n=get(); A=get(); B=get();
for(int i=1;i<=n;i++)
{
a[i]=get();
Sum[i]=Sum[i-1]+a[i];
}

total=Sum[n];
while(total)
{
len++;
total>>=1;
}

if(A!=1) PartOne();
else PartTwo();

printf("%lld",res);

}