BZOJ4069: [Apio2015]巴厘岛的雕塑

按位贪心,显然较大的位能是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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值