BZOJ4828 AHOI/HNOI2017大佬(动态规划+bfs)

  注意到怼大佬的操作至多只能进行两次。我们逐步简化问题。

  首先令f[i][j]表示第i天结束后自信值为j时至多有多少天可以进行非防御操作(即恢复自信值之外的操作)。这个dp非常显然。由于最终只需要保证存活,那么取f中的最大值即可(可以在第n天之前使大佬自信值为0而结束),之后就不用再管自己的自信值。复杂度是O(n·mc),数据范围远远没有开满,可能是怕提示做法吧。

  现在知道了有多少天可以用来攻击(当然可以有些天划水)。对于攻击操作,哪一天进行是没有区别的。那么先不考虑还嘴操作。

  只剩下怼大佬的操作了。先看一次。虽然值域很大,操作方案数也是指数级别,但发现总共能打出来的在值域范围内的伤害种类数在可以接受的范围内。具体有多少我也不知道,总之我们可以暴搜剪剪枝map判个重(伤害和等级都相等),把所有可行伤害算出来。

  于是知道了用x天怼大佬可以造成的所有可行伤害。最多可以怼两次,那么给两次怼操作分配天数,当然还有还嘴操作。

  假设总共n天,给两次怼操作分配的天数分别为d1,d2,造成的伤害分别为w1,w2。如果能怼死大佬,则其满足w1+w2<=q<=w1+w2+n-(d1+d2)。这样不用考虑n的总天数限制。如果枚举d1和w1,由前一个式子,随着w1减小,满足条件的最大的w2会增大。而由后一个式子,显然应让w2-d2尽量大。于是我们按照w排序,从大到小枚举w1,并记录w2-d2的最大值就可以了。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
#define N 110
#define inf 100000001
int n,m,Q,a[N],w[N],f[N][N],head,tail;
map<long long,bool> g;
struct data
{
    int d,l,w;
    bool operator <(const data&a) const
    {
        return w<a.w;
    }
}q[10000010];
long long calc(int x,int y){return 1ll*(n+1)*x+y;}
void bfs()
{
    q[tail=1]=(data){1,0,1};g[calc(1,0)]=1;
    do
    {
        head++;if (q[head].d==n) break;
        if (q[head].l>1&&1ll*q[head].w*q[head].l<inf&&!g[calc(q[head].w*q[head].l,q[head].l)]) g[calc(q[head].w*q[head].l,q[head].l)]=1,q[++tail]=(data){q[head].d+1,q[head].l,q[head].w*q[head].l};
        if (1ll*q[head].w*(q[head].l+1)<inf&&!g[calc(q[head].w,q[head].l+1)]) g[calc(q[head].w,q[head].l+1)]=1,q[++tail]=(data){q[head].d+1,q[head].l+1,q[head].w};
    }while (head<tail);
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj4828.in","r",stdin);
    freopen("bzoj4828.out","w",stdout);
    const char LL[]="%I64d";
#else
    const char LL[]="%lld";
#endif
    n=read(),Q=read(),m=read();
    for (int i=1;i<=n;i++) a[i]=read();
    for (int i=1;i<=n;i++) w[i]=read();
    memset(f,200,sizeof(f));
    f[0][m]=0;
    for (int i=0;i<n;i++)
        for (int j=a[i+1];j<=m;j++)
        {
            f[i+1][min(m,j-a[i+1]+w[i+1])]=max(f[i+1][min(m,j-a[i+1]+w[i+1])],f[i][j]);
            f[i+1][j-a[i+1]]=max(f[i+1][j-a[i+1]],f[i][j]+1);
        }
    int v=0;
    for (int i=1;i<=n;i++) 
        for (int j=0;j<=m;j++)
        v=max(v,f[i][j]);
    n=v;
    bfs();
    sort(q+1,q+tail+1);
    q[0].d=q[0].l=q[0].w=0;
    while (Q--)
    {
        int x=read(),mx=-inf,p=-1;
        int flag=0;
        for (int j=tail;j>=1;j--)
        {
            while (p<tail&&q[p+1].w+q[j].w<=x) 
            p++,mx=max(mx,q[p].w-q[p].d);
            if (q[j].w-q[j].d+mx+n>=x) {flag=1;break;}
        }
        cout<<flag<<endl;
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Gloid/p/9419886.html