#Problem
把 nnn 个数分成 xxx 组,求每组和的最小按位或值(A<=x=BA<=x=BA<=x=B)
#Solution
错误dp…以此警示…
f[i][j]f[i][j]f[i][j] 表示前 iii 个数,分成 jjj 段的最小按位或值
f[i][j]=min(f[k][j−1]∣sum(k+1,i))f[i][j]=min(f[k][j-1]|sum(k+1,i))f[i][j]=min(f[k][j−1]∣sum(k+1,i))
虽说按位或会越来越小,但是可能出现或的内个值特别大,而前面我们如果取一个小的数,倒不如去一个大的数是后面都是0
考虑数位 dpdpdp ,我们考虑这一位是否能是 000 ,也就是一个 bndbndbnd 的限制,结果就是 bndbndbnd 取个反就好啦
对于 n<=100n<=100n<=100 的情况,我们定义 f[i][j]f[i][j]f[i][j] 表示前 iii 个数分成 jjj 段能否满足 bndbndbnd 的限制(即某些位确定为 000)。然后 bndbndbnd 成立的条件就是 f[n][A]f[n][A]f[n][A]~f[n][B]f[n][B]f[n][B] 中有成立的即可
复杂度为 O(n3log)O(n^3log)O(n3log)
但对于最后一个子任务…显然上面内个复杂度是做不了的
然而有一个特例 A=1A=1A=1
我们可以省去一维枚举分成的段数。用 dp[i]dp[i]dp[i] 表示前 iii 个数满足 bndbndbnd 条件的最少分的段数。
如果 dp[n]<=Bdp[n]<=Bdp[n]<=B 也就是说在满足 bndbndbnd 这个条件下,分的段数可以成立。
这样我们就可以把这道题解决了
#Code
#include <cstdio>
#include <cstring>
#include <map>
#include <algorithm>
using namespace std;
#define N 110
#define ll long long
#define inf 1ll<<60
int n,A,B,cnt=0,a[2010];
ll f[N][N],dp[2010];
ll sum[2010],ans=inf;
inline char gc(){
static char buf[1<<16],*S,*T;
if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
return *S++;
}
inline int read(){
int x=0,f=1;char ch=gc();
while(ch<'0' || ch>'9'){if(ch=='-') f=-1;ch=gc();}
while('0'<=ch && ch<='9') x=x*10+ch-'0',ch=gc();
return x*f;
}
inline bool dp1(ll bnd){
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;k++){
if(f[k][j-1] && !((sum[i]-sum[k])&bnd)){
f[i][j]=1;
}
}
for(int i=A;i<=B;i++) if(f[n][i]) return 1;
return 0;
}
inline void solve1(){
ll bnd=0;
for(int i=cnt-1;i>=0;i--){
bnd|=1ll<<i;
if(!dp1(bnd)) bnd^=1ll<<i;
}
printf("%lld\n",(1ll<<cnt)-1-bnd);
}
inline bool dp2(ll bnd){
for(int i=1;i<=n;i++) dp[i]=inf;dp[0]=0;
for(int i=1;i<=n;i++)
for(int j=0;j<i;++j)
if(dp[j]!=inf && !((sum[i]-sum[j])&bnd)){
dp[i]=min(dp[i],dp[j]+1);
}
if(dp[n]<=B) return 1;
return 0;
}
inline void solve2(){
ll bnd=0;
for(int i=cnt-1;i>=0;i--){
bnd|=1ll<<i;
if(!dp2(bnd)) bnd^=1ll<<i;
}
printf("%lld\n",(1ll<<cnt)-1-bnd);
}
int main(){
freopen("a.in","r",stdin);
n=read();A=read();B=read();
for(int i=1;i<=n;i++) a[i]=read(),sum[i]=sum[i-1]+a[i];
ll x=sum[n];
while(x) cnt++,x>>=1;
if(A==1) solve2();
else solve1();
return 0;
}