zoukankan      html  css  js  c++  java
  • P5299[PKUWC2018]Slay the Spire【dp】

    前言


    正题

    题目链接:https://www.luogu.com.cn/problem/P5299


    题目大意

    \(2n\)张牌,

    • \(n\)张强化牌,每张上有一个正整数\(x(x>1)\),如果使用后之后的每一张攻击牌伤害都会乘上\(x\)
    • \(n\)张攻击牌,每张上有一个正整数\(x\),使用后造成\(x\)点伤害。

    随机抽上来\(m\)张,然后按照最优策略打出\(k\)张的情况下,求所有情况造成的伤害和。

    \(1\leq k\leq m\leq 2n\leq 3000\)


    解题思路

    考虑一个最优策略是啥,显然地我们有强化牌肯定优先打出,直到打完或者只剩最后一费。

    因为翻倍至少多一倍的伤害,而我们攻击牌肯定是从大往小选,所以不可能一张攻击牌使得伤害翻倍。
    先把两种牌按照数组从大到小排序
    我们可以分为两种情况讨论

    • 打出\(k-1\)张强化牌和一张攻击牌
    • 打出\(<k-1\)张强化牌和若干张攻击牌

    第一种情况我们设\(f_i\)表示选出了\(i\)张强化牌的所有方案中前\(k\)张牌乘积的和。
    然后枚举一个在\(k-1\sim m\)之间的数字\(i\)表示抽到了\(i\)张强化牌,然后再枚举攻击力最大的一张攻击牌,剩下的方案用组合数计算即可。

    第二种情况比较麻烦,同样的设\(f_{0,i}\)表示抽了\(i(i<k)\)张强化牌的所有方案中所有牌的乘积和。然后设\(f_{i,j}\)表示总共选了\(i\)张攻击牌和强化牌,打出了前\(k\)张强化牌和攻击牌时所有强化牌乘积的和,\(g_{i,j}\)则表示造成的伤害和。
    然后转移即可。

    时间复杂度:\(O(nm)\)


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const ll N=1e4,P=998244353;
    ll T,n,m,k,a[N],b[N],f[N],g[N],fac[N],inv[N],ans;
    ll C(ll n,ll m){
    	if(m>n)return 0;
    	return fac[n]*inv[m]%P*inv[n-m]%P;
    }
    signed main()
    {
    	inv[0]=fac[0]=inv[1]=1;
    	for(ll i=2;i<N;i++)inv[i]=P-inv[P%i]*(P/i)%P;
    	for(ll i=1;i<N;i++)fac[i]=fac[i-1]*i%P,inv[i]=inv[i-1]*inv[i]%P;
    	scanf("%d",&T);
    	while(T--){
    		scanf("%lld%lld%lld",&n,&m,&k);ans=0;
    		for(ll i=1;i<=n;i++)scanf("%lld",&a[i]);
    		for(ll i=1;i<=n;i++)scanf("%lld",&b[i]);
    		for(ll i=0;i<=m;i++)f[i]=g[i]=0;f[0]=1;
    		sort(a+1,a+1+n);reverse(a+1,a+1+n);
    		sort(b+1,b+1+n);reverse(b+1,b+1+n);
    		for(ll i=1,x;i<=n;i++)
    			for(ll j=m;j>=1;j--){
    				if(j<k)(f[j]+=f[j-1]*a[i]%P)%=P;
    				else (f[j]+=f[j-1])%=P;
    			}
    		for(ll i=k-1;i<m;i++){
    			for(ll j=1;j<=n;j++)
    				(ans+=f[i]*b[j]%P*C(n-j,m-i-1)%P)%=P;
    			f[i]=0;
    		}
    		for(ll i=1;i<=n;i++){
    			for(ll j=m;j>=1;j--){
    				(f[j]+=f[j-1])%=P;
    				if(j<=k)(g[j]+=g[j-1]+b[i]*f[j-1]%P)%=P;
    				else (g[j]+=g[j-1])%=P;
    			}
    		}
    		printf("%lld\n",(ans+g[m])%P);
    	}
    	return 0;
    }
    
  • 相关阅读:
    hbase单机安装和简单使用
    工作随记--div最小高度
    工作随记——弹出QQ联系方式
    关于vs2012解决方案中项目DLL文件引用问题
    工作随想——框架之我见
    jQuery 选择表格(table)里的行和列及改变简单样式
    ASP.NET的Get和Post方式的区别归纳总结
    C# 静态类与非静态类、静态成员的区别
    引用静态资源的url添加版本号,解决版本发布后的浏览器缓存有关问题
    python技巧 一等函数
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/15572313.html
Copyright © 2011-2022 走看看