• 【PKUWC2018】Slay the Spire


    Description

    九条可怜在玩一个很好玩的策略游戏:Slay the Spire,一开始九条可怜的卡组里有 (2n) 张牌,每张牌上都写着一个数字(w_i),一共有两种类型的牌,每种类型各 (n) 张:

    1. 攻击牌:打出后对对方造成等于牌上的数字的伤害。

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

    现在九条可怜会等概率随机从卡组中抽出 (m) 张牌,由于费用限制,九条可怜最多打出 (k) 张牌,假设九条可怜永远都会采取能造成最多伤害的策略,求她期望造成多少伤害。

    假设答案为 ( ext{ans}),你只需要输出

    [left ( ext{ans} imes frac{(2n)!}{m!(2n-m)!} ight) ~mod 998244353 ]

    即可

    其中 (x!) 表示 (prod_{i=1}^{x}i),特别地,(0!=1)

    Input

    第一行一个正整数 (T) 表示数据组数

    接下来对于每组数据:

    第一行三个正整数 (n,m,k)

    第二行 (n) 个正整数 (w_i),表示每张强化牌上的数值。

    第三行 (n) 个正整数 (w_i),表示每张攻击牌上的数值。

    Output

    输出 (T) 行,每行一个非负整数表示每组数据的答案。

    Solution

    据说是去年的签到题啊。。九条可怜竟然会出签到题!

    但是这道题我还是想了很久。。

    首先我们要考虑最优的出牌策略。

    有加强肯定先加强,留着加强牌不出先出攻击牌肯定是不优的。。因为加强牌的数值都大于(1)

    那么我们出牌肯定是从大到小出加强牌,当出完或者出了(k-1)张后。考虑从大到小出攻击牌。这样就能使伤害最多

    考虑DP

    因为每种牌都是从大到小出的。。不妨我们先将每种牌按从大到小的顺序排序

    (G_{i,j})表示前(i)加强牌,选了(j)张,第(i)张牌必须选的不同方案乘积和,显然有

    [G_{i,j}=sum_{k=j-1}^{i-1} {G_{k,j-1} * w_i} ]

    同理,有(F_{i,j})表示前(i)攻击牌,选了(j)张,第(i)张牌必须选的不同方案的攻击力和,有

    [F_{i,j}=sum_{k=j-1}^{i-1} {F_{k,j-1}+C_{k}^{j-1} * w_i} ]

    用前缀和优化后复杂度为(O(n^2))

    考虑统计答案

    我们枚举在(m)张牌中有几张加强牌,设加强牌数量为(t)

    (t leq k-1),意味着我们可以打完所有的加强牌再打攻击牌。

    (k leq t),则我们只能从大到小出(k-1)张加强牌后打一张权值最大的攻击牌

    配合组合数计算即可

    Code

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int N=3010,Mod=998244353;
    int F[N][N],G[N][N],add_sum[N],pro_sum[N],mul_sum[N],J[N],R[N];
    int T,n,m,k,w[N],atk[N],ans;
    int read()
    {
    	int ans=0;char ch=getchar();
    	while (ch<'0' || ch>'9') ch=getchar();
    	while (ch>='0' && ch<='9') ans=ans*10+ch-'0',ch=getchar();
    	return ans;
    }
    int C(int x,int y)
    { 
    	return (x>=y?1LL*J[x]*R[y]%Mod*R[x-y]%Mod:0);
    }
    int fpow(int a,int k)
    {
    	int ans=1;
    	while (k)
    	{
    		if (k&1) ans=1LL*ans*a%Mod;
    		a=1LL*a*a%Mod;
    		k>>=1;
    	}
    	return ans;
    }
    bool cmp(int a,int b)
    {
    	return a>b;
    }
    void init()
    {
    	ans=0;
    	memset(F,0,sizeof(F));
    	memset(G,0,sizeof(G));
    	memset(add_sum,0,sizeof(add_sum));
    	memset(pro_sum,0,sizeof(pro_sum));
    	memset(mul_sum,0,sizeof(mul_sum));
    }
    int main()
    {
    	J[0]=1;
    	for (int i=1;i<=3000;i++) J[i]=1LL*J[i-1]*i%Mod;
    	for (int i=0;i<=3000;i++) R[i]=fpow(J[i],Mod-2);
    	T=read();
    	while (T--)
    	{
    		init();
    		n=read(),m=read(),k=read();
    		for (int i=1;i<=n;i++) w[i]=read();
    		for (int i=1;i<=n;i++) atk[i]=read();
    		sort(w+1,w+n+1,cmp);
    		sort(atk+1,atk+n+1,cmp);
    		mul_sum[0]=1;
    		G[0][0]=1;
    		for (int i=1;i<=n;i++)
    		{
    			for (int j=1;j<=min(i,k-1);j++)
    				G[i][j]=(G[i][j]+1LL*mul_sum[j-1]*w[i]%Mod)%Mod;
    			for (int j=1;j<=min(i,k-1);j++)
    				mul_sum[j]=(mul_sum[j]+G[i][j])%Mod;
    		}
    		pro_sum[0]=1;
    		for (int i=1;i<=n;i++)
    		{
    			for (int j=1;j<=min(i,k);j++)
    				F[i][j]=((F[i][j]+1LL*pro_sum[j-1]*atk[i]%Mod)%Mod+add_sum[j-1])%Mod;
    			for (int j=1;j<=min(i,k);j++)
    			{
    				add_sum[j]=(add_sum[j]+F[i][j])%Mod;
    				pro_sum[j]=(pro_sum[j]+C(i-1,j-1))%Mod;
    			}
    		}
    		for (int i=0;i<=min(n,m-1);i++)
    		{
    			if (m-i>n) continue;
    			if (i<k)
    			{
    				int num=0,res=k-i;
    				for (int j=i;j<=n;j++) num=(num+G[j][i])%Mod;
    				for (int j=res;j<=n;j++) 
    					ans=(ans+1LL*num*F[j][res]%Mod*C(n-j,m-k)%Mod)%Mod;
                                            //因为要保证res张攻击牌只能在前j张产生。所以要使得m-k张攻击牌的权值小于前j张
    			}
    			else
    			{
    				int num=0;
    				for (int j=k-1;j<=n;j++) num=(num+1LL*G[j][k-1]*C(n-j,i-(k-1))%Mod)%Mod;
    				for (int j=1;j<=n;j++)
    					ans=(ans+1LL*num*F[j][1]%Mod*C(n-j,m-i-1)%Mod)%Mod;//同理
    			}
    		}
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    谈谈年度最佳代码“不管你们信不信,反正我信了”
    如何:用对称密钥对 XML 元素进行加密
    WPF 线程间访问控件
    xml和模型对象之间的序列化和反序列化
    [转]汇编语言的准备知识给初次接触汇编者 3
    asp.net mvc 自定义路由 【asp.net mvc 自学笔记】
    [转]汇编语言的准备知识给初次接触汇编者 2
    sftpd 启动 报错: vsftpd:500 OOPS: bad bool value in config file for: anonymous_enable
    Linux iptables配置FTP的主动和被动模式
    cacti安装完后就停留在登陆界面,输入默认的用户名密码登陆不进去!
  • 原文地址:https://www.cnblogs.com/Code-Geass/p/10289432.html
Copyright © 2020-2023  润新知