BZOJ.5467.[PKUWC2018]Slay the Spire(DP)

LOJ
BZOJ
洛谷
php

哪張能力牌能乘攻擊啊,太nb了叭git


顯然若是有能力牌,那麼應該選最大的儘量的打出\(k-1\)張。
而後下面說的指望都是乘總方案數後的,即全部狀況的和。而後\(w_i\)統一用\(A_i\)表示了。優化

\(Solution1\)

因此考慮枚舉最終抽到了幾張能力牌。那麼咱們要算:\(F(n,m)\)表示抽到\(n\)張攻擊牌,打出最大的\(m\)張的指望傷害;\(G(n,m)\)表示抽到\(n\)張能力牌,打出最大的\(m\)張的指望倍數。
考慮怎麼算\(F(n,m)\)。不妨枚舉攻擊最小的那張是什麼。也就是把攻擊牌從大到小排序,\(f[i][j]\)表示在前\(i\)張中選\(j\)張的指望傷害,其中第\(i\)張必定取。轉移就再枚舉一下次小的,\(f[i][j]=C_{i-1}^{j-1}A_i+\sum\limits_{k=j-1}^{i-1}f[k][j-1]\)
\(G(n,m)\)同理。令\(g[i][j]\)表示從前\(i\)大的能力牌中選\(j\)張的指望倍數,其中第\(i\)張必定取。\(g[i][j]=A_i\sum\limits_{k=j-1}^{i-1}g[k][j-1]\)
兩個DP均可之前綴和優化。因此複雜度是\(O(n^2)\)的。spa

那麼\(F,G\)的轉移一樣枚舉選出來的最小的那張,\(F(n,m)=\sum\limits_{i=m}^{N}C_{N-i}^{n-m}f[i][m]\)\(G(n,m)=\sum\limits_{i=m}^{N}C_{N-i}^{n-m}g[i][m]\)。對於單個\(F/G(n,m)\)的計算是\(O(n)\)的。
而後\(Ans=\sum\limits_{i=1}^{k-1}G(i,i)F(m-i,k-i)+\sum\limits_{i=k}^NG(i,k-1)F(m-i,1)\)。咱們只會用到\(O(n)\)\(F/G\)的值,現算一下便可,總複雜度是\(O(n^2)\)的。code

爲了方便應該把\(f/g\)的一二維交換下=-=。排序

\(Solution2\)

\(f[i][j]\)表示考慮了前\(i\)張牌,抽到了\(j\)張攻擊牌的指望傷害。確定是選出最大的若干張打出。將牌從小到大排序,而後根據\(j\)肯定這張牌選不選,有\[f[i][j]=f[i-1][j]+C_{i-1}^{j-1}A_i+\begin{cases}0,&m-j\geq k-1\\f[i-1][j-1],&m-j<k-1\end{cases}\]get

考慮\(i\)的貢獻,\(m-j\geq k-1\)時,只能取一張攻擊牌,那取\(i\)就能保證打出的是最大的那張;不然取完\(i\)以後還能夠在取\(j-1\)張的狀況。而後再統計上之前的只考慮\(i-1\)張牌時的\(f[i-1][j]\)(注意這個=-=)。
\(g[i][j]\)表示考慮了前\(i\)張牌,抽到了\(j\)張能力牌的指望倍數。由於是選最大的\(\min(k-1,\ j)\)張打出,因此把能力牌從大到小排序,有\[g[i][j]=g[i-1][j]+\begin{cases}g[i-1][j-1]*A_i,&j<k\\g[i-1][j-1],&j\geq k\end{cases}\]string

就是\(j<k\)時用\(A_i\),不然抽到\(A_i\)也不用。
那麼答案就是\(\sum_{i=0}^mf[n][i]g[n][m-i]\)啦。
複雜度也是\(O(n^2)\),不過常數要更小。it


可是懶得再寫代碼了,代碼是\(Sol1\)的。否則仍是有把握搞個BZOJRank1.2的。io

//27376kb   9684ms
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#include <functional>
#define mod 998244353
#define Mod(x) x>=mod&&(x-=mod)
#define Add(x,v) (x+=v)>=mod&&(x-=mod)
#define gc() getchar()
typedef long long LL;
const int N=1505;

int A[N],B[N],f[N][N],g[N][N],C[N][N];

inline int read()
{
    int now=0;register char c=gc();
    for(;!isdigit(c);c=gc());
    for(;isdigit(c);now=now*10+c-48,c=gc());
    return now;
}
int F(int n,int m,int N)
{
    LL ans=0; int *f=::f[m];
    for(int i=m; i<=N; ++i) ans+=1ll*C[N-i][n-m]*f[i]%mod;
    return ans%mod;
}
int G(int n,int m,int N)
{
    LL ans=0; int *g=::g[m];
    for(int i=m; i<=N; ++i) ans+=1ll*C[N-i][n-m]*g[i]%mod;
    return ans%mod;
}

int main()
{
    C[0][0]=1;
    for(int Ts=read(),mx=0; Ts--; )
    {
        int n=read(),m=read(),K=read();
        if(mx<n)
        {
            for(int i=mx+1; i<=n; ++i)
            {
                C[i][i]=C[i][0]=1;
                for(int j=1; j<i; ++j) C[i][j]=C[i-1][j-1]+C[i-1][j], Mod(C[i][j]);
            }
            mx=n;
        }
        for(int i=1; i<=n; ++i) A[i]=read();
        for(int i=1; i<=n; ++i) B[i]=read();
        std::sort(A+1,A+1+n,std::greater<int>()), std::sort(B+1,B+1+n,std::greater<int>());

        g[0][0]=1;//!! K=1時會用到啊=-= 
        for(int i=1; i<=n; ++i) f[1][i]=B[i], g[1][i]=A[i];//or not
        for(int i=2,lim=std::min(n,m); i<=lim; ++i)
        {
            int sf=0,sg=0;
            for(int j=i; j<=n; ++j)
                Add(sf,f[i-1][j-1]), f[i][j]=(1ll*C[j-1][i-1]*B[j]+sf)%mod,
                Add(sg,g[i-1][j-1]), g[i][j]=1ll*A[j]*sg%mod;
        }
        LL ans=0;
        for(int i=std::max(0,m-n); i<=n&&i<=m; ++i)//m-i<=n 枚舉的是能力牌 能夠是0啊=v= 
            if(i<K) ans+=1ll*G(i,i,n)*F(m-i,K-i,n)%mod;
            else ans+=1ll*G(i,K-1,n)*F(m-i,1,n)%mod;
        printf("%lld\n",ans%mod);
    }

    return 0;
}
相關文章
相關標籤/搜索