BZOJ4828: [Hnoi2017]大佬

138 篇文章 0 订阅
14 篇文章 0 订阅

首先我们每一回合不选择苟活(刷水题)的话,我们能做的事和这是第几个回合无关
那我们肯定希望有尽量多的回合做苟活之外的事
先DP一下我们在存活的情况下,最多有多少个回合有空
然后这些回合里,我们可以打1,升级或发大招
然后…我们最多操作n次,C<=1e8(题解说)状态数是不多的
(用bfs+hash,实测100次时搜出的状态是700w+)
然后对于这m个大佬,每个可以 O(n) 判断这些状态两两组合能不能凑出C(加入0状态,设s为伤害,i为需要的回合数,k为可以操作的回合数,那么C-s1-s2+i1+i2<=k,所以C+(-s1+i1)-s2+i2<=k,记录一下左指针历史(-s1+i1)的最小值,和当前的右指针判是否可行)

总复杂度 O(nmc+bfs+nm)

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

inline void up(int &x,const int &y){if(x<y)x=y;}
const int maxm = 22;
const int maxn = 110;
const ll inf = 1e8;
const int maxh = 8000000;
const int hashmod = 7160117;

int n,m,mc,C;
int a[maxn],w[maxn];
int f[maxn][maxn];

int q[maxh],tail;
struct Hash
{
    int s,x,i;
    Hash(){s=0;}
    Hash(const int _s,const int _x,const int _i){s=_s;x=_x;i=_i;}
}h[maxh],s[maxh];
inline int get_hash(const int s,const int x){return ((ll)s*233333+x)%hashmod;}
void ins(const int s,const int x,const int i)
{
    int hi=get_hash(s,x);
    while(h[hi].s)
    {
        if(h[hi].s==s&&h[hi].x==x) return;
        hi++;
        if(hi==maxh) hi=0;
    }
    h[hi].s=s; h[hi].x=x; h[hi].i=i; q[++tail]=hi;
}
inline bool cmp(const Hash x,const Hash y){return x.s==y.s?x.i<y.i:x.s<y.s;}

int main()
{
    scanf("%d%d%d",&n,&m,&mc);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++) scanf("%d",&w[i]);

    int mx=0;
    memset(f,-1,sizeof f);
    f[0][mc]=0;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<=mc;j++) if(f[i][j]!=-1)
            if(j-a[i+1]>=0) 
                up(f[i+1][j-a[i+1]],f[i][j]+1),
                up(f[i+1][min(mc,j-a[i+1]+w[i+1])],f[i][j]);
    }
    for(int i=1;i<=n;i++) for(int j=0;j<=mc;j++) up(mx,f[i][j]);

    h[q[tail=1]=1]=Hash(1,0,1);
    for(int i=1;i<=tail;i++)
    {
        const int x=q[i];
        if((ll)h[x].s*h[x].x>inf||h[x].i>=mx) continue;

        if(h[x].x) ins(h[x].s*h[x].x,h[x].x,h[x].i+1);
        ins(h[x].s,h[x].x+1,h[x].i+1);
    }
    //printf("%d\n",tail);
    for(int i=1;i<=tail;i++) s[i]=h[q[i]];
    sort(s+1,s+tail+1,cmp);
    int hn=0;
    s[0].s=s[1].s-1;
    for(int i=1;i<=tail;i++)
        if(s[i].s!=s[i-1].s) h[++hn]=s[i];
    h[0]=Hash(0,0,0);

    while(m--)
    {
        scanf("%d",&C);
        int p1=0,p2=hn;
        bool flag=false; int mn=INT_MAX;
        for(;h[p1].s<=C;p1++)
        {
            if(p1>p2) break;
            while(h[p1].s+h[p2].s>C) p2--;
            int tk=h[p1].i-h[p1].s;
            if(mn>tk) mn=tk;
            int k=C+mn-h[p2].s+h[p2].i;
            if(k<=mx) { flag=true; break; }
        }
        if(flag) puts("1");
        else puts("0");
    }

    return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值