[LOJ2538][PKUWC2018]Slay the Spire:DP

分析

学会新姿势!我们可以通过调整DP顺序来体现选取物品的优先顺序!

显然选取强化牌的最优策略是倍数从高到低,能选就选,最多选\(k-1\)张,选取攻击牌的最优策略是伤害从高到低,尽量少选,但最少选\(1\)张。

我们可以把强化牌从大到小排序,把攻击牌从小到大排序,令\(f[i][j]\)表示考虑了最大的\(i\)张强化牌,其中所有可选的强化牌有\(j\)张的情况的最优策略下的强化倍数和,\(g[i]\)表示考虑了最小的\(i\)张攻击牌,其中所有可选的攻击牌有\(j\)张的情况的最优策略下的伤害和。

状态转移方程如下:

\[f[i][j]=f[i-1][j]+f[i-1][j-1] \times a[i]\ (j \leq k-1)\]

\[f[i][j]=f[i-1][j]+f[i-1][j-1]\ (j > k-1)\]

\[g[i][j]=b[i] \times \binom{i-1}{j-1}\ (m-j \geq k-1)\]

\[g[i][j]=b[i] \times \binom{i-1}{j-1}+g[i-1][j-1]\ (m-j < k-1)\]

代码

#include <bits/stdc++.h>

#define rin(i,a,b) for(int i=(a);i<=(b);++i)
#define irin(i,a,b) for(int i=(a);i>=(b);--i)
#define trav(i,a) for(int i=head[a];i;i=e[i].nxt)
#define Size(a) (int)a.size()
#define pb push_back
#define mkpr std::make_pair
#define fi first
#define se second
#define lowbit(a) ((a)&(-(a)))
typedef long long LL;

using std::cerr;
using std::endl;

inline int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

const int MAXN=3005;
const int MOD=998244353;

int n,m,k,a[MAXN],b[MAXN],f[MAXN],g[MAXN];
int fac[MAXN],invf[MAXN];

inline int qpow(int x,int y){
    int ret=1,tt=x%MOD;
    while(y){
        if(y&1)ret=1ll*ret*tt%MOD;
        tt=1ll*tt*tt%MOD;
        y>>=1;
    }
    return ret;
}

inline int binom(int n,int m){
    if(n<0||m<0||n<m)return 0;
    return 1ll*fac[n]*invf[n-m]%MOD*invf[m]%MOD;
}

void init(){
    fac[0]=1;rin(i,1,n)fac[i]=1ll*fac[i-1]*i%MOD;
    invf[n]=qpow(fac[n],MOD-2);irin(i,n-1,0)invf[i]=1ll*invf[i+1]*(i+1)%MOD;
}

int main(){
    n=3000;init();
    int T=read();
    while(T--){
        n=read(),m=read(),k=read();
        rin(i,1,n)a[i]=read();
        rin(i,1,n)b[i]=read();
        std::sort(a+1,a+n+1);
        std::sort(b+1,b+n+1); 
        rin(i,0,m)f[i]=g[i]=0;
        f[0]=1,g[0]=0;
        irin(i,n,1)irin(j,std::min(n-i+1,m),1){
            if(j<=k-1)f[j]=(f[j]+1ll*f[j-1]*a[i])%MOD;
            else f[j]=(f[j]+f[j-1])%MOD;
        }
        rin(i,1,n)irin(j,std::min(i,m),1){
            if(m-j<k-1)g[j]=(g[j]+1ll*binom(i-1,j-1)*b[i]+g[j-1])%MOD;
            else g[j]=(g[j]+1ll*binom(i-1,j-1)*b[i])%MOD;
        }
        int ans=0;
        rin(i,0,m)ans=(ans+1ll*f[i]*g[m-i])%MOD;
        printf("%d\n",ans);
    }
    return 0;
}

posted on 2019-05-09 09:56 ErkkiErkko 阅读(...) 评论(...) 编辑 收藏

统计