9.25DAY1T2

2.巴厘岛的雕塑4401

(sculpture)

【问题描述】

  印尼巴厘岛的公路上有许多的雕塑,我们来关注它的一条主干道。 
  在这条主干道上一共有N座雕塑,为方便起见,我们把这些从1到N连续地进行标号,其中第i座雕塑的年龄是Yi年。为了使这条路的环境更加优美,政府想把这些雕塑分成若干组,并通过在组与之间种上一些树,来吸引更多的游客来巴厘岛。 
  下面是将雕塑分组的规则: 
  这些雕塑必须被分为恰好X组,其中A≤X≤B,每组必须含有至少一个雕塑,每个雕塑也必须属于且只一组。同一组中的所有雕塑必须 位于这条路的连续一段上。 
  当雕塑被分好组后,对于每个组,我们首先计算出该组所有雕塑的年龄和,然后计算将每组年龄和按位取或(即对上述年龄和按位取或),我们把按位取或后得到的结果称为这一分组的最终优美度(颜值)。 
  请问政府能得到的最小终优美度(颜值)是多少? 
  备注: 
  将两个非负数P和Q按位取或是这样进行计算的: 
  首先把P和Q转换成二进制:设nP是P的二进制位数,nQ是Q的二进制位数,M为nP和nQ中的最大值。P的二进制表示为pM-1,pM-2,…,p1,p0,Q的二进制表示为qM-1,qM-2,…,q1,q0,其中pi和qi分别是P和Q二进制表示下的第i位,第M-1位是数的最高位,第0位是数的最低位。 
  P与Q按位取或后的结果是:(pM-1或qM-1)(pM-2或qM-2)…(p1或q1)(p0或q0)。其中 : 
    0 或 0 = 0 
    0 或 1 = 1 
    1 或 0 = 1 
    1 或 1 = 1

【输入】

  输入的第一行包含三个用空格分开的整数N,A和B。 
  第二行包含N个用空格分开的整数Y1,Y2,...,YN。

【输出】

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

【输入样例】

6 1 3 8 1 2 1 5 4

【输出样例】

11

【样例说明】

  将这些雕塑分为2组,(8,1,2)和(1,5,4),它们的和是(11)和(10),最终优美是(11或10)=11。(不难验证,这也是最终优美度的最小值。) 
【数据规模与约定】
  共有五部分数据(或称5个子任务)。 
  第1部分数据占9分,数据范围满足:1≤N≤20,1≤A≤B≤N,0≤Yi≤1,000,000,000; 
  第2部分数据占16分,数据范围满足:1≤N≤20,1≤A≤B≤min(20,N),0≤Yi≤10; 
  第3部分数据占21分,数据范围满足:1≤N≤100,A=1,1≤B≤min(20,N),0≤Yi≤20; 
  第4部分数据占25分,数据范围满足:1≤N≤100,1≤A≤B≤N,0≤Yi≤1,000,000,000; 
  第5部分数据占29分,数据范围满足:1≤N≤2000,A=1,1≤B≤N,0≤Yi≤1,000,000,000。

 

 

"显然位运算的极值问题都应该从高位向低位考虑。优先让这一位为0,如果行的话这一位就是0,否则就设为1。" 

设答案为ans,从高位到低位枚举 是否有使ans的这一位为0的方案,注意到每一位是互相独立的。假设枚举到了倒数第x位, 即ans的最高位到倒数第x+1位的最优01分布已确定,现在正在判断第x位是否有可能填0:

对于每个x,考虑递推法:

设 布尔数组 f[i][j]表示:将前i个数分j段,能否在 得到的优美度的最高位到倒数第x+1位 都与ans一致的情况下,让倒数第x位为0 因为段与段之间的合并为or运算,所以

递推方式为:

若 将前k个数(k<i)分j-1段,得到的优美度本身与ans一致,并且第j段的优美度也与  ans一致,才可以由f[k][j-1]转移到f[i][j] 如何判断以上两个条件:

1. 得到的优美度的最高位到倒数第k+1位 是否与ans一致:( (S[i]-S[k])>>k | ans ) == ans (ans为0的位,S[i]-S[k]都不为1)

2. 得到的优美度的倒数第k位能否为0:( (S[i]-S[k]) & 1<<(k-1) ) == 0 对于每个x,若f[n][A~B]有至少一个为1,nas的第k位就可以为0

 复杂度:O( logY * n^3 )

对于最后一组数据:A==1,B<=n,段数只有上限  我们要想把f数组的第二个维度省掉的话,用f[i]记录将前i个数分段并得到可行解的最小段数,最后判断其是否小于B,即可。 

复杂度:O( logY * n^2 )

注意:1写成1LL,开全局ll

code:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 typedef long long ll;
 6 long long len=0;
 7 long long s[1000005],f[2005][2005],g[200005]; 
 8 long long n,a,b,ans=0;
 9 void work1(){
10     for(int x=len;x>=1;x--){
11         memset(g,0x3f3f3f3f,sizeof g);
12         g[0]=0;
13         for(int i=1;i<=n;i++){
14             for(int j=0;j<i;j++){
15                 if(g[j]<b){
16                     int t=s[i]-s[j];
17                     if((t>>(ll)x|ans)==ans&&(t&(1ll<<(ll)(x-1)))==0)g[i]=min(g[i],g[j]+1); 
18                 }
19             }
20         }
21         if(g[n]>b){
22             ans<<=1;
23             ans++;
24         }
25         else {
26             ans<<=1;
27         }
28     }
29 }
30 void work2(){
31     for(long long x=len;x>=1;x--){
32         memset(f,0,sizeof f);
33         f[0][0]=1;
34         for(long long i=1;i<=n;i++){
35             for(long long j=1;j<=i;j++){
36                 for(long long k=0;k<i;k++){
37                     if(f[k][j-1]){
38                         long long t=s[i]-s[k];
39                         if(((t>>(ll)x)|ans)==ans&& (t&(1ll<<x-1ll))==0)f[i][j]=1;
40                     }
41                 }
42             }
43         }
44         int i;
45         for(i=a;i<=b;i++){
46               if(f[n][i])
47                   break;
48         } 
49         ans<<=1;
50         if(i>b)ans++;
51     //    cout<<ans<<endl;
52     }
53 }
54 int main(){
55     cin>>n>>a>>b;
56     for(long long i=1;i<=n;i++){
57         cin>>s[i];
58         s[i]+=s[i-1];
59     }
60     for(int t=s[n];t>0;t>>=1)
61         len++;
62     if(a==1)work1();
63     else work2();
64     cout<<ans;
65 }

 

posted @ 2018-09-25 15:24  saionjisekai  阅读(39)  评论(0编辑  收藏  举报