我们不难发现,假设抽了x张攻击牌,y张强化牌,那么肯定是打出尽可能多张的强化牌后,再开始出攻击牌(当然最少要一张攻击牌)

我们设G(i,j)表示:所有(抽到的攻击牌牌数为i,打出的攻击牌牌数为j)的方案,所产生的攻击值的总和

形式化地说:​$G(i,j)=\sum\limits_{S∈攻击牌\and|S|=i}S中前j大的牌的攻击值之和$

考虑到G(i,j)难以一次求出,我们考虑设置一些中间变量

设g[i][j]表示:我们对攻击牌从小大大进行排序,目前选了i张牌,其中最小的牌是第j张的总贡献,其中b[j]表示第j小的牌的攻击值

当i=1时,有$g[i][j]=b[j]$

当i>1时,有$g[i][j]=b[j]\times \binom{n-j+1}{i}+\sum\limits_{k=j+1}^{n} g[i-1][k]$

后面那个$\sum$我们可以通过求前缀和,实现快速求解

考虑如何通过g(i,j)求解G(i,j)

不难推出:$G(i,j)=\sum\limits_{k=1}^{n-j+1} g[i][k]$

我们设F(i,j)表示:所有(抽到的强化牌牌数为i,打出的强化牌数为j)的方案里,每一个方案所产生的贡献(抽到的i张牌中,最大的j张牌之积)的和

形式化地说:$F(i,j)=\sum\limits_{S∈强化牌\and|S|=i}S中前j大的牌的强化值之积$

考虑到F(i,j)难以一次求出,我们考虑设置一些中间变量

设f[i][j]表示:我们对强化牌从小大大进行排序,目前选了i张牌,其中最小的牌是第j张的总贡献,其中a[j]表示第j小的牌的攻击值

当i=1时,有$f[i][j]=a[j]$

当i>1时,有$f[i][j]=a[j]\times \sum\limits_{k=j+1}^{n} f[i-1][k]$

后面那个$\sum$我们可以通过求前缀和,实现快速求解

考虑如何通过f(i,j)求解F(i,j)

不难推出:$F(i,j)=\sum\limits_{k=1}^{n-j+1} f[i][k]$

最后我们要求的答案ans,假设当前,我们抽到的强化牌数量为i

若i<k,则$ans+=F(i,i)\times G(m-i,k-i)$

若i≥k,则$ans+=F(i,k-1)\times G(m-i,1)$

完结撒花~

 1 #include <algorithm>
2 #include <iostream>
3 #include <cstring>
4 #include <cstdio>
5 using namespace std;
6 typedef long long ll;
7 const int mod=998244353;
8 int T, n, m, k, a[1505], b[1505], c[3005][3005], f[1505][1505];
9 int g[1505][1505], sum[1505];
10 int F(int x, int y){
11 if(x<y) return 0;
12 if(!y) return c[n][x];
13 int re=0;
14 for(int j=x-y+1; j<=n-y+1; j++)
15 re = (re + (ll)f[y][j]*c[j-1][x-y]%mod) % mod;
16 //感性理解一下……大概就是把x-y张不打出的牌放到j前头,这里我讲不太清QAQ
17 return re;
18 }
19 int G(int x, int y){
20 if(x<y) return 0;
21 int re=0;
22 for(int j=x-y+1; j<=n-y+1; j++)
23 re = (re + (ll)g[y][j]*c[j-1][x-y]%mod) % mod;
24 return re;
25 }
26 int main(){
27 cin>>T;
28 for(int i=0; i<=3000; i++){
29 c[i][0] = 1;
30 for(int j=1; j<=i; j++)
31 c[i][j] = (c[i-1][j-1] + c[i-1][j]) % mod;
32 }
33 while(T--){
34 memset(f, 0, sizeof(f));
35 memset(g, 0, sizeof(g));
36 scanf("%d %d %d", &n, &m, &k);
37 for(int i=1; i<=n; i++) scanf("%d", &a[i]);
38 for(int i=1; i<=n; i++) scanf("%d", &b[i]);
39 sort(a+1, a+1+n);//跟牌的顺序无关,可以sort
40 sort(b+1, b+1+n);
41 for(int i=1; i<=n; i++){
42 f[1][i] = a[i];//初始化f[][],显然只选1张的倍率之和是a[i]
43 sum[i] = (sum[i-1] + a[i]) % mod;//前缀和,方便转移
44 }
45 for(int i=2; i<=n; i++){
46 for(int j=1; j<=n-i+1; j++)
47 f[i][j] = (ll)a[j] * (sum[n]-sum[j]+mod) % mod;
48 //打了i张牌,最前头的是第j张,那它就是f[i-1][j+1..n]的和再乘上第j号元素。这个转移的思想是枚举在打了i-1张牌的时候最前头的是哪一张
49 for(int j=1; j<=n; j++)
50 sum[j] = (sum[j-1] + f[i][j]) % mod;
51 }
52 for(int i=1; i<=n; i++){
53 g[1][i] = b[i];
54 sum[i] = (sum[i-1] + b[i]) % mod;
55 }
56 for(int i=2; i<=n; i++){
57 for(int j=1; j<=n-i+1; j++)
58 g[i][j] = ((ll)b[j]*c[n-j][i-1]%mod+(sum[n]-sum[j]+mod)%mod) % mod;
59 //打了i张牌,最前头的是第j张。注意g代表的是(不加强化的)攻击力和。在这种情况下,打了i-1张牌的总情况是c[n-j][i-1]种(j+1..n中选i-1个的方案数),这是第一项;第二项就是继承自g[i-1][j+1..n]
60 for(int j=1; j<=n; j++)
61 sum[j] = (sum[j-1] + g[i][j]) % mod;
62 }
63 int ans=0;
64 for(int i=0; i<m; i++)
65 if(i<k) ans = (ans + (ll)F(i,i)*G(m-i,k-i)%mod) % mod;
66 else ans = (ans + (ll)F(i,k-1)*G(m-i,1)%mod) % mod;
67 printf("%d\n", ans);
68 }
69 return 0;
70 }

【loj2538】 【PKUWC 2018】Slay the Spire dp的更多相关文章

  1. LOJ #2538. 「PKUWC 2018」Slay the Spire (期望dp)

    Update on 1.5 学了 zhou888 的写法,真是又短又快. 并且空间是 \(O(n)\) 的,速度十分优秀. 题意 LOJ #2538. 「PKUWC 2018」Slay the Spi ...

  2. loj2538 「PKUWC 2018」Slay the Spire

    pkusc 快到了--做点题涨涨 rp. ref我好菜啊QAQ. 可以发现期望只是一个幌子.我们的目的是:对于所有随机的选择方法(一共 \(\binom{2n}{m}\)种),这些选择方法都最优地打出 ...

  3. 【BZOJ5251】【八省联考2018】劈配(网络流,二分答案)

    [BZOJ5251][八省联考2018]劈配(网络流,二分答案) 题面 洛谷 BZOJ Description 一年一度的综艺节目<中国新代码>又开始了. Zayid从小就梦想成为一名程序 ...

  4. 【BZOJ5248】【九省联考2018】一双木棋(搜索,哈希)

    [BZOJ5248][九省联考2018]一双木棋(搜索,哈希) 题面 BZOJ Description 菲菲和牛牛在一块n行m列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手.棋局开始时,棋盘上没有任何 ...

  5. LOJ #2540. 「PKUWC 2018」随机算法(概率dp)

    题意 LOJ #2540. 「PKUWC 2018」随机算法 题解 朴素的就是 \(O(n3^n)\) dp 写了一下有 \(50pts\) ... 大概就是每个点有三个状态 , 考虑了但不在独立集中 ...

  6. 【LOJ2542】【PKUWC 2018】随机游走 min-max容斥 树上高斯消元

    题目描述 有一棵 \(n\) 个点的树.你从点 \(x\) 出发,每次等概率随机选择一条与所在点相邻的边走过去. 有 \(q\) 次询问,每次询问给定一个集合 \(S\),求如果从 \(x\) 出发一 ...

  7. 【Asia Yokohama Regional Contest 2018】Arithmetic Progressions

    题目大意:给定 N(1<N<=5000) 个不同元素组成的集合,求从中选出若干数字组成的等差数列最长是多少. 题解:直接暴力有 \(O(n^3)\) 的算法,即:枚举等差数列的前两个值,再 ...

  8. 【LOJ】#2538. 「PKUWC2018」Slay the Spire

    题解 由于强化卡都是大于1的,我们分析一下就会发现,尽可能多的用强化卡,至少用一张攻击卡,一定是每组卡牌的最优选择 所以我们把攻击卡和强化卡从大到小排序 我们设\(g[i][j]\)表示前i张卡牌里选 ...

  9. LOJ2538 PKUWC2018 Slay the Spire DP

    传送门 不想放题面了,咕咕咕咕咕 这个期望明明是用来吓人的,其实要算的就是所有方案的最多伤害的和. 首先可以知道的是,能出强化牌就出强化牌(当然最后要留一张攻击牌出出去),且数字尽量大 所以说在强化牌 ...

随机推荐

  1. 【python基础】第11回 数据类型内置方法 02

    本章内容概要 列表内置方法 字典内置方法 元组内置方法 集合内置方法 可变类型与不可变类型 本章内容详细 1.列表内置方法 list 列表在调用内置方法之后不会产生新的值 1.1 统计列表中的数据值的 ...

  2. jieba分词原理解析:用户词典如何优先于系统词典

    目标 查看jieba分词组件源码,分析源码各个模块的功能,找到分词模块,实现能自定义分词字典,且优先级大于系统自带的字典等级,以医疗词语邻域词语为例. jieba分词地址:github地址:https ...

  3. python实现人脸关键部位检测(附源码)

    人脸特征提取 本文主要使用dlib库中的人脸特征识别功能. dlib库使用68个特征点标注出人脸特征,通过对应序列的特征点,获得对应的脸部特征.下图展示了68个特征点.比如我们要提 取眼睛特征,获取3 ...

  4. C++ 练气期之指针所指何处

    1. 指针 指针是一种C++数据类型,用来描述内存地址. 什么是内存地址? 内存中的每一个存储单元格都有自己的地址,地址是使用二进制进行编码.地址从形态上看是一个整型数据类型.但是,它的数据含义并不表 ...

  5. private关键字的作用及使用和this关键字的作用

    封装的操作--private关键字 private的含义 1. private是一个权限修饰符,代表最小权限. 2. 可以修饰成员变量和成员方法. 3. 被private修饰后的成员变量和成员方法,只 ...

  6. 地址解析协议(ARP) 分析

    什么是ARP协议 ARP(A ddress R esolution P rotocol)- 地址解析协议 ,用于将IP地址解析为MAC地址.复杂来说,ARP用于32位IPv4地址和以太网的48位MAC ...

  7. c# 添加指定扩展名的系统右键菜单(Windows11以前)

    在上篇文章c# 添加系统右键菜单(Windows11以前)中我们说了怎么在文件夹上增加一个菜单项,但是我们可能还需要给某个单独的扩展名添加右键菜单. 这里我们不用常见的扩展名来做,我们新做一个.jx的 ...

  8. Hadoop中HDFS 的相关进程以及工作流程图(详细流程图)

  9. jdbc 12: 模糊查询

    jdbc连接mysql,进行模糊查询 package com.examples.jdbc.o11_模糊查询; import com.examples.jdbc.utils.DBUtils; impor ...

  10. HashSet集合存储数据的结构(哈希表)和Set集合存储㢝不重复的原理

    HashSet集合存储数据的结构(哈希表) Set集合存储㢝不重复的原理 前提:存储的元素必须重写hashCode方法和equals方法