【BZOJ】4069: [Apio2015]巴厘岛的雕塑-dp

题解

是位或和啊,(看成异或了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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值