按位贪心,显然较大的位能是0就是0,然后就从高到低枚举每一位。dp判定这一位是否可以填0。
dp[i][j]表示长度为i,分了j段当前位是否可以是0,枚举k转移,如果(s[i]-s[k])这一段这一位是0 && (s[i]-s[j])这一段没有超过原来已经确定的答案的范围,就可以转移。复杂度n^3logn
注意到最后的数据n<=2000好像过不了?但是保证了A==1,那么就dp[i]表示当前位是0最少需要多少段,枚举转移即可,最后判断dp[n]<=B,复杂度n^2logn。
最重要的是发现自己的程序比LH大爷的慢一倍,而且HEOI2016又被卡了60pts常数sad…,于是和LH大爷学习了下高端的常数优化技巧:
先判定耗时较少的,比如dp[k][j-1]就比judge(s[i]-s[j])要快的多,如果许多状态没用就不要继续转移。
这样优化掉了2s的常数跪LH大爷…
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
//by:MirrorGray
using namespace std;
ll s[2111],ans,wei,tmp;
inline bool judge(ll x){
x>>=wei;x<<=wei;
return (x|ans) == ans;
}
int main(){
// freopen("sculpture.in","r",stdin);
// freopen("sculpture.out","w",stdout);
int n,A,B;scanf("%d%d%d",&n,&A,&B);
for(int i=1;i<=n;i++)scanf("%lld",&s[i]),s[i]+=s[i-1];
tmp=s[n];while(tmp)wei++,tmp>>=1;
if(A==1){
static int dp[2111];
for(;~wei;wei--){
memset(dp,0x3f,sizeof(dp));
dp[0]=0;
for(int i=1;i<=n;i++)for(int k=0;k<i;k++)
if(dp[k]+1<dp[i] && judge(s[i]-s[k]))dp[i]=dp[k]+1;
if(dp[n]>B)ans|=1ll<<wei;
}
printf("%lld\n",ans);
}
else{
static bool dp[111][111];
for(;~wei;wei--){
memset(dp,0,sizeof(dp));dp[0][0]=true;
for(int i=1;i<=n;i++)for(int j=1;j<=i&&j<=B;j++)for(int k=0;k<i&&!dp[i][j];k++)
if( dp[k][j-1] && (~(s[i]-s[k])&(1ll<<wei)) && judge(s[i]-s[k]))dp[i][j]=true;
bool ok=1;
for(int i=A;i<=B&&ok;i++)if(dp[n][i])ok=false;
if(ok)ans|=1ll<<wei;
}
printf("%lld\n",ans);
}
return 0;
}