一、题目意思

  1. 游戏规则:小苯和小红各有n张牌,所有牌是1~2n的不重复数字(排列)。每轮两人出最前面的牌,数字大的得分并扔掉这张牌,另一方牌不变;有人没牌就结束。

  2. 小苯可以随便重新排自己的牌,目标是让自己得分最多。求有多少种这样的排法(不重排也算一种),答案对998244353取模。

  3. 关键:小红的牌顺序固定,只有小苯能换牌序;多组测试数据,要快读。

二、核心思路

小苯的最大得分是多少?

先找小红手里最小的牌(记为min_b)。小苯手里比min_b大的牌有m张,那他最多能得m分。

原因:小红最小的牌都打不过小苯的m张牌,而小红其他牌都比min_b大,小苯剩下的牌打不过任何一张,所以最多赢m轮。

怎么算合法排法数?

只要把小苯那m张能赢的牌随便排,剩下n-m张牌也随便排,就能拿到最高分。

排法数 = m张牌的排法数 × (n-m)张牌的排法数。

补充:n张牌的排法数是n!(n的阶乘,比如3! = 3×2×1=6),提前算好所有可能用到的阶乘,避免重复计算。

三、代码逐行解析

#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;
typedef vector<ll> vll;
#define endl '\n'
const int M = 998244353;
const int N = 2e5
ll num[N]; // 存阶乘
void fun() { 
    num[0] = 1; // 0的阶乘是1
    for (int i = 1; i < N; i++) {
        num[i] = num[i-1] * i % M; // 递推算阶乘
    }
}

ll num[N];
void fun() {
	num[0] = 1;
	for (int i = 1; i < N; i++) {
		num[i] = num[i - 1] * i % M;
	}
}

int main() {
	IOS;
	fun();
	int t;
	cin >> t;
	while (t--) {
		int n;
		cin >> n;
		vi a(n), b(n);
		for (int i = 0; i < n; i++) cin >> a[i];
		for (int i = 0; i < n; i++) cin >> b[i];
		sort(b.begin(), b.end());
		int min_b = b[0];//找到最小的数min_b
		int m = 0;//统计个数
		for (int i = 0; i < n; i++) {
			if (a[i] > min_b) m++;
		}
		ll c = num[m] * num[n - m] % M;// // 答案是m! * (n-m)! 取模
		cout << c << endl;
	}
	return 0;
}

四、示例

示例1

输入:n=2,a=[1,2],b=[3,4]

  1. 给b排序:[3,4],min_b=3。

  2. 统计a中比3大的数:1和2都不大,m=0。

  3. 答案:0! * 2! = 1 * 2 = 2。

示例2

输入:n=4,a=[1,8,7,2],b=[3,6,4,5]

  1. 给b排序:[3,4,5,6],min_b=3。

  2. 统计a中比3大的数:8、7,m=2。

  3. 答案:2! * 2! = 2 * 2 = 4。