pkusc 快到了……做點題漲漲 rp。
 ref我好菜啊QAQ。
可以發現期望只是一個幌子。我們的目的是:對於所有隨機的選擇方法(一共 \(\binom{2n}{m}\)種),這些選擇方法都最優地打出 \(k\) 張牌,他們能造成的傷害的和是多少。
顯然的是,能打強化就打強化(不過你好歹也要攻擊一張)。記 \(m\) 張卡中分給強化卡的數量為 \(i\)。我們枚舉 \(i\),根據 \(i\) 與 \(k\) 的大小關系來決定怎樣打牌。
那么 \(i < k\) 時,就打出 \(i\) 張強化卡,\(k-i\) 張攻擊卡。(這意味着你能打的強化卡總共才 \(i\) 張,當然是能打強化卡就打強化卡)
\(i \geq k-1\) 時,就打出 \(k-1\) 張強化卡,\(1\) 張攻擊卡。(這意味着你能打的強化卡還挺多,留一張攻擊就行了)。
記 \(F(i,j)\) 為分給強化卡的數量為 \(i\),打出 \(j\) 張,所有方案翻的倍率的和。\(G(i,j)\) 為分給攻擊卡的數量為 \(i\),打出 \(j\) 張,所有方案的(不加強化的)攻擊力和。
兩者分別對應 \(F(i,i) \times G(m-i,k-i)\) 和 \(F(i,k-1) \times G(m-i,1)\)。為什么可以是這種“和乘和”的形式呢?因為乘法分配律。
現在的問題變成快速計算 \(F\) 和 \(G\)。
關於 \(F\),可以 sort 以后定義一個 \(f\),\(f(i,j)\) 表示選(注意是選,不是分)了 \(i\) 張強化牌且最這 \(i\) 張牌中位置靠前的那張牌是所有強化牌中的第 \(j\) 個,這樣的所有方案翻的倍率的和。轉移看代碼。\(G\) 和 \(g\) 也類似。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
const int mod=998244353;
int T, n, m, k, a[1505], b[1505], c[3005][3005], f[1505][1505];
int g[1505][1505], sum[1505];
int F(int x, int y){
	if(x<y)	return 0;
	if(!y)	return c[n][x];
	int re=0;
	for(int j=x-y+1; j<=n-y+1; j++)
		re = (re + (ll)f[y][j]*c[j-1][x-y]%mod) % mod;
    //感性理解一下……大概就是把x-y張不打出的牌放到j前頭,這里我講不太清QAQ
	return re;
}
int G(int x, int y){
	if(x<y)	return 0;
	int re=0;
	for(int j=x-y+1; j<=n-y+1; j++)
		re = (re + (ll)g[y][j]*c[j-1][x-y]%mod) % mod;
	return re;
}
int main(){
	cin>>T;
	for(int i=0; i<=3000; i++){
		c[i][0] = 1;
		for(int j=1; j<=i; j++)
			c[i][j] = (c[i-1][j-1] + c[i-1][j]) % mod;
	}
	while(T--){
		memset(f, 0, sizeof(f));
		memset(g, 0, sizeof(g));
		scanf("%d %d %d", &n, &m, &k);
		for(int i=1; i<=n; i++)	scanf("%d", &a[i]);
		for(int i=1; i<=n; i++)	scanf("%d", &b[i]);
		sort(a+1, a+1+n);//跟牌的順序無關,可以sort
		sort(b+1, b+1+n);
		for(int i=1; i<=n; i++){
			f[1][i] = a[i];//初始化f[][],顯然只選1張的倍率之和是a[i]
			sum[i] = (sum[i-1] + a[i]) % mod;//前綴和,方便轉移
		}
		for(int i=2; i<=n; i++){
			for(int j=1; j<=n-i+1; j++)
				f[i][j] = (ll)a[j] * (sum[n]-sum[j]+mod) % mod;
            //打了i張牌,最前頭的是第j張,那它就是f[i-1][j+1..n]的和再乘上第j號元素。這個轉移的思想是枚舉在打了i-1張牌的時候最前頭的是哪一張
			for(int j=1; j<=n; j++)
				sum[j] = (sum[j-1] + f[i][j]) % mod;
		}
		for(int i=1; i<=n; i++){
			g[1][i] = b[i];
			sum[i] = (sum[i-1] + b[i]) % mod;
		}
		for(int i=2; i<=n; i++){
			for(int j=1; j<=n-i+1; j++)
				g[i][j] = ((ll)b[j]*c[n-j][i-1]%mod+(sum[n]-sum[j]+mod)%mod) % mod;
            //打了i張牌,最前頭的是第j張。注意g代表的是(不加強化的)攻擊力和。在這種情況下,打了i-1張牌的總情況是c[n-j][i-1]種(j+1..n中選i-1個的方案數),這是第一項;第二項就是繼承自g[i-1][j+1..n]
			for(int j=1; j<=n; j++)
				sum[j] = (sum[j-1] + g[i][j]) % mod;
		}
		int ans=0;
		for(int i=0; i<m; i++)
			if(i<k)	ans = (ans + (ll)F(i,i)*G(m-i,k-i)%mod) % mod;
			else	ans = (ans + (ll)F(i,k-1)*G(m-i,1)%mod) % mod;
		printf("%d\n", ans);
	}
	return 0;
}
 
         
         
       