loj2538 「PKUWC2018」Slay the Spire 【dp】

题目链接

loj2538

题解

比较明显的是,由于强化牌倍数大于\(1\),肯定是能用强化牌尽量用强化牌
如果强化牌大于等于\(k\),就留一个位给攻击牌

所以我们将两种牌分别排序,企图计算\(F(i,j)\)表示\(i\)张强化牌选出最强的\(j\)张的所有方案的倍数和
\(G(i,j)\)表示从\(i\)张攻击牌选出最强\(j\)张的所有方案的伤害和

那么

\[ans = \sum\limits_{i = 0}^{k - 1} F(i,i)G(m - i,k - i) + \sum\limits_{i = k}^{m} F(i,k - 1)G(m - i,1) \]

所以我们只需计算出\(F\)\(G\)
\(F\)为例,我们枚举选出最后一张牌是什么
那么设\(f[i][j]\)表示用了\(i\)张强化牌,最后一张是\(j\)的倍数和
同样设\(g[i][j]\)表示用了\(i\)张攻击牌,最后一张是\(j\)的伤害和
那么有

\[f[i][j] = w_j\sum\limits_{x = 0}^{j - 1}f[i - 1][x] \]

\[g[i][j] = w_j{j - 1 \choose i - 1} + \sum\limits_{x = 0}^{j - 1}g[i - 1][x] \]

可用前缀和优化为\(O(n^2)\)

那么

\[F(x,y) = \sum\limits_{i = 0}^{n}f[y][i]{n - i \choose x - y} \]

\[G(x,y) = \sum\limits_{i = 0}^{n}g[y][i]{n - i \choose x - y} \]

总复杂度\(O(Tn^2)\)

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define mp(a,b) make_pair<int,int>(a,b)
#define cls(s) memset(s,0,sizeof(s))
#define cp pair<int,int>
#define LL long long int
using namespace std;
const int maxn = 3005,maxm = 100005,INF = 1000000000,P = 998244353;
inline int read(){
	int out = 0,flag = 1; char c = getchar();
	while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
	while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
	return out * flag;
}
int n,m,K,fac[maxn],fv[maxn],inv[maxn];
int w1[maxn],w2[maxn];
int f[maxn][maxn],g[maxn][maxn];
int s[maxn];
void init(){
	fac[0] = fac[1] = inv[0] = inv[1] = fv[0] = fv[1] = 1;
	for (int i = 2; i <= 3000; i++){
		fac[i] = 1ll * fac[i - 1] * i % P;
		inv[i] = 1ll * (P - P / i) * inv[P % i] % P;
		fv[i] = 1ll * fv[i - 1] * inv[i] % P;
	}
}
inline int C(int n,int m){
	if (n < m) return 0;
	return 1ll * fac[n] * fv[m] % P * fv[n - m] % P;
}
inline int F(int x,int y){
	if (x > n || x < y) return 0;
	int re = 0;
	for (int i = 0; i <= n; i++)
		re = (re + 1ll * f[y][i] * C(n - i,x - y) % P) % P;
	return re;
}
inline int G(int x,int y){
	if (x > n || x < y) return 0;
	int re = 0;
	for (int i = 0; i <= n; i++)
		re = (re + 1ll * g[y][i] * C(n - i,x - y) % P) % P;
	return re;
}
inline bool cmp(const int& a,const int& b){
	return a > b;
}
void work(){
	sort(w1 + 1,w1 + 1 + n,cmp);
	sort(w2 + 1,w2 + 1 + n,cmp);
	for (int i = 0; i <= n; i++)
		for (int j = 0; j <= n; j++)
			f[i][j] = g[i][j] = 0;
	f[0][0] = 1;
	s[0] = 1;
	for (int i = 1; i <= n; i++) s[i] = s[i - 1];
	for (int i = 1; i <= n; i++){
		for (int j = i; j <= n; j++){
			f[i][j] = 1ll * w1[j] * s[j - 1] % P;
		}
		for (int j = 0; j < i; j++) s[j] = 0;
		for (int j = i; j <= n; j++) s[j] = (s[j - 1] + f[i][j]) % P;
	}
	s[0] = 0;
	for (int i = 1; i <= n; i++) s[i] = s[i - 1];
	for (int i = 1; i <= n; i++){
		for (int j = i; j <= n; j++){
			g[i][j] = (1ll * w2[j] * C(j - 1,i - 1) % P + s[j - 1]) % P;
		}
		for (int j = 0; j < i; j++) s[j] = 0;
		for (int j = i; j <= n; j++) s[j] = (s[j - 1] + g[i][j]) % P;
	}
	int ans = 0;
	for (int i = 0; i <= min(n,m); i++){
		int j = m - i; if (j < 0 || j > n) continue;
		if (i < K){
			ans = (ans + 1ll * F(i,i) * G(j,K - i) % P) % P;
		}
		else{
			ans = (ans + 1ll * F(i,K - 1) * G(j,1) % P) % P;
		}
	}
	printf("%d\n",ans);
}
int main(){
	init();
	int T = read();
	while (T--){
		n = read(); m = read(); K = read();
		REP(i,n) w1[i] = read();
		REP(i,n) w2[i] = read();
		work();
	}
	return 0;
}

posted @ 2018-06-23 08:32  Mychael  阅读(182)  评论(0编辑  收藏  举报