印尼巴厘岛的公路上有许多的雕塑,我们来关注它的一条主干道。
在这条主干道上一共有 NN 座雕塑,为方便起见,我们把这些雕塑从 11 到 NN 连续地进行标号,其中第 ii 座雕塑的年龄是 YiYi 年。为了使这条路的环境更加优美,政府想把这些雕塑分成若干组,并通过在组与组之间种上一些树,来吸引更多的游客来巴厘岛。
下面是将雕塑分组的规则:
- 这些雕塑必须被分为恰好 XX 组,其中 A≤X≤BA≤X≤B,每组必须含有至少一个雕塑,每个雕塑也必须属于且只属于一个组。同一组中的所有雕塑必须位于这条路的连续一段上。
- 当雕塑被分好组后,对于每个组,我们首先计算出该组所有雕塑的年龄和。
- 计算所有年龄和按位取或的结果。我们这个值把称为这一分组的最终优美度。
请问政府能得到的最小的最终优美度是多少?
备注:将两个非负数 PP 和 QQ 按位取或是这样进行计算的:
- 首先把 PP 和 QQ 转换成二进制。
- 设 nPnP 是 PP 的二进制位数,nQnQ 是 QQ 的二进制位数,MM 为 nPnP 和 nQnQ 中的最大值。PP 的二进制表示为 pM−1pM−2…p1p0pM−1pM−2…p1p0,QQ 的二进制表示为 qM−1qM−2…q1q0qM−1qM−2…q1q0,其中 pipi 和 qiqi 分别是 PP 和 QQ 二进制表示下的第 ii 位,第 M−1M−1 位是数的最高位,第 00 位是数的最低位。
- PP 与 QQ 按位取或后的结果是: (pM−1ORqM−1)(pM−2ORqM−2)…(p1ORq1)(p0ORq0)(pM−1ORqM−1)(pM−2ORqM−2)…(p1ORq1)(p0ORq0)。其中:
- 0OR0=00OR0=0
- 0OR1=10OR1=1
- 1OR0=11OR0=1
- 1OR1=11OR1=1
输入格式
输入的第一行包含三个用空格分开的整数 N,A,BN,A,B。
第二行包含 NN 个用空格分开的整数 Y1,Y2,…,YNY1,Y2,…,YN。
输出格式
输出一行一个数,表示最小的最终优美度。
样例一
input
6 1 3 8 1 2 1 5 4
output
11
explanation
将这些雕塑分为 22 组,(8,1,2)(8,1,2) 和 (1,5,4)(1,5,4),它们的和是 (11)(11) 和 (10)(10),最终优美度是 (11OR10)=11(11OR10)=11。(不难验证,这也是最终优美度的最小值。)
子任务
- 子任务 1 (9 分)
- 1≤N≤201≤N≤20
- 1≤A≤B≤N1≤A≤B≤N
- 0≤Yi≤10000000000≤Yi≤1000000000
- 子任务 2 (16 分)
- 1≤N≤501≤N≤50
- 1≤A≤B≤min{20,N}1≤A≤B≤min{20,N}
- 0≤Yi≤100≤Yi≤10
- 子任务 3 (21 分)
- 1≤N≤1001≤N≤100
- A=1A=1
- 1≤B≤N1≤B≤N
- 0≤Yi≤200≤Yi≤20
- 子任务 4 (25 分)
- 1≤N≤1001≤N≤100
- 1≤A≤B≤N1≤A≤B≤N
- 0≤Yi≤10000000000≤Yi≤1000000000
- 子任务 5 (29 分)
- 1≤N≤20001≤N≤2000
- A=1A=1
- 1≤B≤N1≤B≤N
- 0≤Yi≤10000000000≤Yi≤1000000000
时间限制:1s
空间限制:64MB
贪心 DP 位运算
“同一组中的所有雕塑必须位于这条路的连续一段上”←这个性质看上去科学而优雅,这使得我们可以DP分段。
答案是位运算的结果,那么可以考虑贪心,显然从高到低考虑每一位,当前位能填0就填0,最后的结果肯定比当前位填1小。
数据范围比较小,可以从高到低分别考虑答案的每一个二进制位:
设f[考虑到第i个][分了j组]=当前位是否为0
简单DP枚举分段位置,如果 f[k][j-1]==0 (k以前的部分都不会使得当前位为1)
且 (sum[i]-sum[k])&(1<<pos)==0 (从k+1到i分成一段可行) 且 (((sum[i]-sum[k])|ans)>>pos) == (ans >> pos) (这么分组并不会改变更高位的答案)
那么f[i][j]=0
最后一组数据n==2000,用上面的方法复杂度太高了。由于A==1,所以可以用另一种DP方程类似地求解,具体看代码
1 /*by SilverN*/ 2 #include<algorithm> 3 #include<iostream> 4 #include<cstring> 5 #include<cstdio> 6 #define LL long long 7 using namespace std; 8 const int mxn=2010; 9 int read(){ 10 int x=0,f=1;char ch=getchar(); 11 while(ch<‘0‘ || ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} 12 while(ch>=‘0‘ && ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();} 13 return x*f; 14 } 15 int f[105][105]; 16 LL a[mxn],smm[mxn]; 17 int g[mxn]; 18 LL ans=0; 19 int n,A,B; 20 void solve(){ 21 for(int pos=45;pos>=0;pos--){ 22 memset(f,1,sizeof f); 23 f[0][0]=0; 24 for(int i=1;i<=n;i++)//now 25 for(int j=1;j<=B;j++)//group 26 for(int k=0;k<i;k++) 27 if(f[k][j-1]==0 && (((smm[i]-smm[k])>>pos)&1)==0 && (((smm[i]-smm[k])|ans)>>pos)==(ans>>pos)){ 28 f[i][j]=0; 29 break; 30 } 31 bool flag=0; 32 for(int i=A;i<=B;i++) 33 if(f[n][i]==0){flag=1;break;} 34 if(!flag)ans|=1LL<<pos; 35 } 36 printf("%lld\n",ans); 37 return; 38 } 39 void solve2(){ 40 for(int pos=51;pos>=0;pos--){ 41 for(int i=1;i<=n;i++)g[i]=0x3f3f3f3f; 42 g[0]=0; 43 for(int i=1;i<=n;i++) 44 for(int j=0;j<i;j++){ 45 if((((smm[i]-smm[j])>>pos)&1))continue; 46 if((((smm[i]-smm[j])|ans)>>pos)==(ans>>pos))g[i]=min(g[i],g[j]+1); 47 } 48 if(g[n]>B)ans|=1LL<<pos; 49 } 50 printf("%lld\n",ans); 51 return; 52 } 53 int main(){ 54 int i,j; 55 n=read();A=read();B=read(); 56 for(i=1;i<=n;i++){ 57 a[i]=read();smm[i]=smm[i-1]+a[i]; 58 } 59 if(A==1)solve2(); else solve(); 60 return 0; 61 }