题解
是位或和啊,(看成异或了orz)
数据范围给得很秒:
子任务 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
不分类讨论会T啊QWQ
从高位到低位dp(二进制下),保证答案最小。
不考虑a=1的情况,我们可以列出f[i][j]=0/1表示前i个数分成j组是否可以满足当前位为0(=1:可以满足;=0:不可满足)
转移条件?从f[k][j-1]可以转移到f[i][j]不仅要满足s[i]-s[k]二进制表示当前位为0,还要满足所有之前更高位必需为0的要求,这个可以用个数记下来,每次位与判断一下。
这是个四维dp(虽然有一维只有40)
对于a=1的情况,我们可以降一个维度。
设dp[i]表示使前i个分组后满足当前位为0的最小组数。
每次判一下f[n]是否小于等于b
代码
#include<cstdio>
#include<algorithm>
#include<cctype>
using namespace std;
typedef long long ll;
const int w=40,N=2002,M=102;
int n,a,b,dp1[M][M],dp2[N];
ll s[N],ans,zero,bin[w+5];
inline int rd()
{
char ch=getchar();int x=0,f=1;
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
return x*f;
}
inline bool jud(ll x,int t){return !(x&zero) && !(x&bin[t]);}
int main(){
int i,j,k,q;
bin[0]=1;for(i=1;i<=w;++i) bin[i]=bin[i-1]<<1;
n=rd();a=rd();b=rd();s[0]=0;
for(i=1;i<=n;++i) s[i]=s[i-1]+(ll)rd();
if(a!=1){
for(k=w;k>=0;--k){
for(i=0;i<=n;++i) for(j=0;j<=b;++j) dp1[i][j]=0;
dp1[0][0]=1;
for(i=1;i<=n;++i)
for(q=1;q<=b;++q)
for(j=0;j<i;++j)
if(dp1[j][q-1] && jud(s[i]-s[j],k)) dp1[i][q]=1;
for(i=a;i<=b;++i) if(dp1[n][i]) break;
i<=b? zero|=bin[k]:ans|=bin[k];
}
}else{
for(k=w;k>=0;--k){
for(i=1;i<=n;++i) dp2[i]=N;
for(i=1;i<=n;++i)
for(j=0;j<i;++j)
if(dp2[j]<N && jud(s[i]-s[j],k)) dp2[i]=min(dp2[i],dp2[j]+1);
dp2[n]<=b ? zero|=bin[k]:ans|=bin[k];
}
}
printf("%lld\n",ans);
}