一、题目意思
-
游戏规则:小苯和小红各有n张牌,所有牌是1~2n的不重复数字(排列)。每轮两人出最前面的牌,数字大的得分并扔掉这张牌,另一方牌不变;有人没牌就结束。
-
小苯可以随便重新排自己的牌,目标是让自己得分最多。求有多少种这样的排法(不重排也算一种),答案对998244353取模。
-
关键:小红的牌顺序固定,只有小苯能换牌序;多组测试数据,要快读。
二、核心思路
小苯的最大得分是多少?
先找小红手里最小的牌(记为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]
-
给b排序:[3,4],min_b=3。
-
统计a中比3大的数:1和2都不大,m=0。
-
答案:0! * 2! = 1 * 2 = 2。
示例2
输入:n=4,a=[1,8,7,2],b=[3,6,4,5]
-
给b排序:[3,4,5,6],min_b=3。
-
统计a中比3大的数:8、7,m=2。
-
答案:2! * 2! = 2 * 2 = 4。

京公网安备 11010502036488号