P3724 [AHOI2017/HNOI2017] 大佬 做题记录

P3724 [AHOI2017/HNOI2017] 大佬

Description

https://www.luogu.com.cn/problem/P3724

Solution

回血和怼大佬没有关系,所以我们将其拆开考虑。

先考虑回血。我们可以先求出自己最多能活着怼大佬多少天。这部分可以用平凡的 dp 求解,并设最大天数为 \(D\)

再考虑怼大佬。技能最多发动两次,我们可以以此为切入点。

对于一个 $F $,我们肯定希望达成它的天数越少越好,以便有更多的时间继续进攻。

我们把所有 \(\leq C_i\) 的且能到达的 \(F_j\) 拿出来,并求出达到每个 \(F_j\) 的最小天数 \(D_j\)

由于 \(F_j\leq 10^8\) 且只含 \(\leq 100\) 的因子,所以 \(F_j\) 的个数不会太多。打表发现只有 \(924572\) 个,约为 \(10^6\)

考虑一个 BFS 的框架,以 \((F,L)\) 为状态求最短路。当 \(F\times L>10^8\) 时不能再拓展,且 \(L\leq 100\),打表发现可拓展的状态只有 \(15039235\) 个,约为 \(2\times 10^7\)

对于大佬 \(C_i\),分为三种情况:

  1. \(D\geq C_i\),可以全用平 A;
  2. \(\exists j,F_j\leq C_j\land F_j+D-D_j\geq C_j\),可以发动一次技能,然后平 A;
  3. \(\exists j,k,F_j+F_k\leq C_i\land F_j+F_k+D-D_j-D_k\geq C_i\),可以发动两次技能,然后平 A。

重点考虑第三种情况。将所有的 \((F_j,D_j)\)\(F_j\) 排序。从左向右枚举 \(j\),此时最大的 \(k\) 也是单调不降的。于是我们再维护一个 \(pre_k\) 表示 \(F_p-D_p\)\([1,k]\) 之间的前缀最大值即可。

int n,m,k;
int a[N],b[N],c[N];
int f[N][N];
int D=-IINF;

const int mod=1000003;

struct HashTable{
    int head[mod+5],tot;

    struct HashNode{
        int x,y,nxt;
    }e[M];

    void Init(){
        memset(head,0,sizeof(head));
        tot=0;
    }

    int GetHash(int x,int y){
        return (1ll*x*10000%mod+y)%mod;
    }

    bool Find(int x,int y){
        int v=GetHash(x,y);
        for(int i=head[v];i;i=e[i].nxt){
            if(e[i].x==x&&e[i].y==y)
                return 1;
        }
        return 0;
    }

    void Ins(int x,int y){
        int v=GetHash(x,y);
        e[++tot]={x,y,head[v]};
        head[v]=tot;
    }
}H;

struct BfsNode{
    int x,y,d;
};
queue<BfsNode> q;
bitset<K> vis;

int tg,h[M];
struct FNode{
    int f,d;
}g[M];

bool Cmp(FNode x,FNode y){return x.f<y.f;}

void Bfs(int C){
    H.Init();
    H.Ins(1,0);
    q.push({1,0,0});
    vis.set(0);
    tg=0;
    while(q.size()){
        int x=q.front().x,y=q.front().y,d=q.front().d;
        q.pop();
        if(d>=D) continue;
        if(!vis[x]){
            g[++tg]={x,d+1};
            vis[x]=1;
        }
        if(!H.Find(x,y+1)){
            q.push({x,y+1,d+1});
            H.Ins(x,y+1);
        }
        if(1ll*x*y<=C&&!H.Find(x*y,y)){
            q.push({x*y,y,d+1});
            H.Ins(x*y,y);
        }
    }
}

signed main(){
    read(n),read(m),read(k);
    int Mx=0;
    for(int i=1;i<=n;i++) read(a[i]);
    for(int i=1;i<=n;i++) read(b[i]);
    for(int i=1;i<=m;i++) read(c[i]),Ckmax(Mx,c[i]);
    memset(f,-0x3f,sizeof(f));
    f[0][k]=0;
    for(int i=1;i<=n;i++){
        for(int j=a[i];j<=k;j++){
            Ckmax(f[i][j-a[i]],f[i-1][j]+1);
            int v=min(k,j-a[i]+b[i]);
            Ckmax(f[i][v],f[i-1][j]);
        }
        for(int j=0;j<=k;j++) Ckmax(D,f[i][j]);
    }
    if(D<=0){
        for(int i=1;i<=m;i++) puts("0");
        return 0;
    }
    Bfs(Mx);
    sort(g+1,g+tg+1,Cmp);
    h[0]=-IINF;
    for(int i=1;i<=tg;i++){
        h[i]=max(h[i-1],g[i].f-g[i].d);
    }
    for(int i=1;i<=m;i++){
        if(D>=c[i]){
            puts("1");
            continue;
        }
        bool ok=0;
        for(int j=1,p=tg;j<=tg;j++){
            if(g[j].f>c[i]) break;
            // printf("J=%d\n",j);
            if(g[j].f+D-g[j].d>=c[i]){ok=1;break;}
            while(p>=1&&g[j].f+g[p].f>c[i]) p--;
            if(p&&g[j].f+D-g[j].d+h[p]>=c[i]){ok=1;break;}
        }
        printf("%d\n",ok);
    }
    return 0;
}
posted @ 2025-06-09 16:42  XP3301_Pipi  阅读(8)  评论(0)    收藏  举报
Title