[bzoj4828] [Hnoi2017]大佬

题目描述

人们总是难免会碰到大佬。他们趾高气昂地谈论凡人不能理解的算法和数据结构,走到任何一个地方,大佬的气场就能让周围的人吓得瑟瑟发抖,不敢言语。你作为一个OIER,面对这样的事情非常不开心,于是发表了对大佬不敬的言论。大佬便对你开始了报复,你也不示弱,扬言要打倒大佬。
现在给你讲解一下什么是大佬,大佬除了是神犇以外,还有着强大的自信心,自信程度可以被量化为一个正整数C(1<=C<=10^8),想要打倒一个大佬的唯一方法是摧毁Ta的自信心,也就是让大佬的自信值等于0(恰好等于0,不能小于0)。由于你被大佬盯上了,所以你需要准备好n(1<=n<=100)天来和大佬较量,因为这n天大佬只会嘲讽你动摇你的自信,到了第n+1天,如果大佬发现你还不服,就会直接虐到你服,这样你就丧失斗争的能力了。
你的自信程度同样也可以被量化,我们用mc (1 <= mc <= 100)来表示你的自信值上限。在第i天(i>=1),大佬会对你发动一次嘲讽,使你的自信值减小a[i],如果这个时刻你的自信值小于0了,那么你就丧失斗争能力,也就失败了(特别注意你的自信值为0的时候还可以继续和大佬斗争)。在这一天,大佬对你发动嘲讽之后,如果你的自信值仍大于等于0,你能且仅能选择如下的行为之一:
1. 还一句嘴,大佬会有点惊讶,导致大佬的自信值C减小1。
2. 做一天的水题,使得自己的当前自信值增加w[i], 并将新自信值和自信值上限mc比较,若新自信值大于mc,则新自信值更新为mc。例如,mc=50,当前自信值为40,若w[i]=5,则新自信值为45,若w[i]=11,则新自信值为50。
3. 让自己的等级值L加1。
4. 让自己的讽刺能力F乘以自己当前等级L,使讽刺能力F更新为F*L。
5. 怼大佬,让大佬的自信值C减小F。并在怼完大佬之后,你自己的等级L自动降为0,讽刺能力F降为1。由于怼大佬比较掉人品,所以这个操作只能做不超过2次。

特别注意的是,在任何时候,你不能让大佬的自信值为负,因为自信值为负,对大佬来说意味着屈辱,而大佬但凡遇到屈辱就会进化为更厉害的大佬直接虐飞你。在第1天,在你被攻击之前,你的自信是满的(初始自信值等于自信值上限mc),你的讽刺能力F是1,等级是0。
现在由于你得罪了大佬,你需要准备和大佬正面杠,你知道世界上一共有m(1<=m<= 20)个大佬,他们的嘲讽时间都是n天,而且第i天的嘲讽值都是a[i]。不管和哪个大佬较量,你在第i天做水题的自信回涨都是w[i]。这m个大佬中只会有一个来和你较量(n天里都是这个大佬和你较量),但是作为你,你需要知道对于任意一个大佬,你是否能摧毁他的自信,也就是让他的自信值恰好等于0。和某一个大佬较量时,其他大佬不会插手。

分析

首先可以设f[i][j]表示前i天,自信值为j,最多能不刷题多少天。不刷题的时候是用来做其它操作的。这样就可以算出最多动多少天,设它是D天。

接着从最多进行两次怼操作入手。设d[f][l]表示达到能力f,等级l,所需的最少天数。保证d[f][l]<D。可以发现它的状态数不多。
然后假设一位dalao的体力为C,那么现在我进行两次怼操作,设它们所需天数、伤害分别是d1,f1、d2,f2。那么满足D-d1-d2≥C-f1-f2时,就是有解的。然而状态数不多,所以可以用个two pointers扫一遍。

#include 
#include 
#include 

using namespace std;

const int N=105,M=11147953,inf=1e8;

typedef long long LL;

int n,m,mc,h[M],tot,a[N],w[N],D,d[M/2][3],F[M],L[M],mi;

int f[N][N];

struct data
{
    int f,d;
}A[M/2];

bool cmp(data a,data b)
{
    return a.f.f;
}

int gethash(int f,int l)
{
    int k=((LL)f*N+l)%M;
    for (;h[k]>0 && (F[k]!=f || L[k]!=l);)
    {
        k++;
        if (k==M) k-=M;
    }
    return k;
}

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]);
    for (int j=0;j<=n;j++) for (int i=0;i<=101;i++) f[j][i]=-n;
    f[0][mc]=0;
    for (int i=1;i<=n;i++)
    {
        for (int j=a[i];j<=mc;j++)
        {
            f[i][j-a[i]]=max(f[i][j-a[i]],f[i-1][j]+1);
            int t=min(mc,j-a[i]+w[i]);
            f[i][t]=max(f[i][t],f[i-1][j]);
        }
    }
    for (int i=0;i<=n;i++) for (int j=0;j<=mc;j++) D=max(D,f[i][j]);
    d[1][0]=1;
    int id=gethash(1,0);
    h[id]=1; F[id]=1; tot=1;
    for (int i=1;i<=tot;i++)
    {
        int f=d[i][0],l=d[i][1],day=d[i][2],id;
        if (day>=D-1) break;
        if (l>0 && inf/lcontinue;
        id=gethash(f,l+1);
        if (h[id]==0)
        {
            h[id]=++tot; F[id]=f; L[id]=l+1; d[tot][0]=f; d[tot][1]=l+1; d[tot][2]=day+1;
        }
        if (!l) continue;
        id=gethash(f*l,l);
        if (h[id]==0)
        {
            h[id]=++tot; F[id]=f*l; L[id]=l; d[tot][0]=f*l; d[tot][1]=l; d[tot][2]=day+1;
        }
    }
    memset(h,0,sizeof(h));
    A[n=1].d=0; A[1].f=0;
    for (int i=1;i<=tot;i++)
    {
        int id=gethash(d[i][0],0);
        if (h[id]==0)
        {
            h[id]=i; F[id]=d[i][0]; L[id]=0; A[++n].d=d[i][2]+1; A[n].f=d[i][0];
        }
    }
    sort(A+1,A+n+1,cmp);
    while (m--)
    {
        int c;
        scanf("%d",&c);
        mi=inf+1;
        int j=1;
        int bz=0;
        for (int i=n;i;i--)
        {
            for (;i>j && A[i].f+A[j].f<=c;j++) if (A[j].d-A[j].f.d-A[j].f;
            if (A[i].d-A[i].f+mi<=D-c)
            {
                bz=1; break;
            }
        }
        printf("%d\n",bz);
    }
    fclose(stdin); fclose(stdout);
    return 0;
}

你可能感兴趣的:(bfs,two,pointers)