zoukankan      html  css  js  c++  java
  • [PKUWC2018] Slay the spire

    Description

    现在有 (n) 张强化牌和 (n) 张攻击牌:

    1. 攻击牌:打出后对对方造成等于牌上的数字的伤害。
    2. 强化牌:打出后,假设该强化牌上的数字为 (x),则其他剩下的攻击牌的数字都会乘上 (x)保证强化牌上的数字都大于 1

    现在等概率地从这 (2n) 张卡中抽出 (m) 张,并且按最优策略打出 (k) 张,问期望能造成多少伤害。

    Solution

    第一这是披着期望皮的计数题

    第二最优策略肯定是能打强化牌就打强化牌,最后剩下一张攻击牌再打

    既然是计数题那我们就要算出每种方案造成的伤害值然后加起来就行了

    (f(i,j,0/1)) 表示前 (i) 张强化牌用 $j $ 张,第 (i) 张用不用的所有方案的乘积的和,(g(i,j,0/1)) 表示前 (i) 张攻击牌用 (j) 张,第 (i) 张用不用的所有方案的和的和

    那么有转移 (f(i,j,0)=f(i-1,j,0)+f(i-1,j,1),f(i,j,1)=f(i,j-1,0)cdot a[i])

    (g(i,j,0)=g(i-1,j,0)+g(i-1,j,1),g(i,j,1)=g(i,j-1,0)+b[i]cdot C_{i-1}^{j-1})

    再设 (F(i,j)) 表示选出 (i) 张强化牌,用了 (j) 张的所有方案的乘积的和,(G(i,j)) 表示选出 (i) 张攻击牌,用了 (j) 张的所有方案的和的和。

    那么显然有 (ans=sumlimits_{i=0}^m egin{cases}F(i,i) imes G(m-i,m-i);;(i<k)\F(i,k-1) imes G(m-i,1);;(ige k)end{cases})

    (F,G) 也可以通过 (f,g) 来求出来,可以枚举断点,即用的第 (j) 张是原序列的第 (x) 张,那么因为选择的是最优策略,原序列的前 (x) 张中一定不多不少选出来了 (j) 张,那剩下的 (i-j) 张就要从 (x+1sim n) 中选出来,拿组合数算一下就行了。具体就是 (F(i,j)=sumlimits_{x=j}^{n-(i-j)} f(x,j,1) imes C_{n-x}^{i-j})

    发现求这个 (F,G)(O(n^3)) 的并不能过去。但是我们也没有必要求出所有的 (F,G) 。只求出需要求的那 (O(n)) 项即可。

    Code

    LOJ格式化代码好丑

    #include<bits/stdc++.h>
    using std::min;
    using std::max;
    using std::swap;
    using std::vector;
    typedef double db;
    typedef long long ll;
    #define pb(A) push_back(A)
    #define pii std::pair<int,int>
    #define all(A) A.begin(),A.end()
    #define mp(A,B) std::make_pair(A,B)
    #define int long long
    const int N=3005;
    const int mod=998244353;
    #define inv(x) ksm(x,mod-2)
    
    int a[N],b[N];
    int n,m,k,C[N][N];
    int f[N][N][2],g[N][N][2];
    
    int getint(){
        int X=0,w=0;char ch=getchar();
        while(!isdigit(ch))w|=ch=='-',ch=getchar();
        while( isdigit(ch))X=X*10+ch-48,ch=getchar();
        if(w) return -X;return X;
    }
    
    void init(int n){
        C[0][0]=1;
        for(int i=1;i<=n;i++){
            C[i][0]=1;
            for(int j=1;j<=i;j++)
                C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
        }
    }
    
    int F(int x,int y){
        int ans=0;
        for(int i=y;i<=n;i++)
            (ans+=f[i][y][1]*C[n-i][x-y]%mod)%=mod;
        return ans;
    }
    
    int G(int x,int y){
        if(!x) return 0;
        int ans=0;
        for(int i=y;i<=n;i++)
            (ans+=g[i][y][1]*C[n-i][x-y]%mod)%=mod;
        return ans;
    }
    
    void solve(){
        n=getint(),m=getint(),k=getint();
        for(int i=1;i<=n;i++) a[i]=getint();
        for(int i=1;i<=n;i++) b[i]=getint();
        memset(f,0,sizeof f);memset(g,0,sizeof g);
        std::sort(a+1,a+1+n),std::sort(b+1,b+1+n);
        std::reverse(a+1,a+1+n),std::reverse(b+1,b+1+n);
        if(k==1){
            int sum=0;
            for(int i=1;i<=n;i++){
                if(2*n-i+1<m) break;
                (sum+=b[i]*C[2*n-i][m-1]%mod)%=mod;
            } printf("%lld
    ",sum);return;
        } f[0][0][1]=1;
        for(int i=1;i<=n;i++){
            for(int j=0;j<=i;j++){
                f[i][j][0]=(f[i-1][j][0]+f[i-1][j][1])%mod;
                g[i][j][0]=(g[i-1][j][0]+g[i-1][j][1])%mod;
                if(j) f[i][j][1]=f[i][j-1][0]*a[i]%mod;
                if(j) g[i][j][1]=(g[i][j-1][0]+b[i]*C[i-1][j-1]%mod)%mod;
            }
        } int ans=0;
        for(int i=0;i<=m and i<=n;i++){
            if(m-i>n) continue;
            if(i<k) (ans+=F(i,i)*G(m-i,k-i)%mod)%=mod;
            else (ans+=F(i,k-1)*G(m-i,1)%mod)%=mod;
        } printf("%lld
    ",ans);
    }
    
    signed main(){
        init(3000);
        int T=getint();
        while(T--) solve();
        return 0;
    }
    
  • 相关阅读:
    静态查找表和动态查找表
    内存分配
    常用不等式
    考研线性代数(向量,线性方程组)
    考研线性代数(矩阵)
    考研线性代数(行列式)
    微积分常用思想方法小结
    bug修复集合(不定期更新)
    上下文对象及servletContext接口
    手动编解码解决get提交错误的问题
  • 原文地址:https://www.cnblogs.com/YoungNeal/p/10300666.html
Copyright © 2011-2022 走看看