链接:https://ac.nowcoder.com/acm/contest/120561/B 来源:牛客网

题目描述

小苯正在和小红玩卡牌游戏。游戏中有 2×n 张牌,每张牌上都有一个数字,所有牌的数字恰好构成了一个长度为 2×n 的排列。 游戏最初时, 2×n 张卡牌被恰好平均分成了两组 n 张牌,并分别发给了两人,小苯第 i 张牌上的数字是 𝑎 𝑖 ,而小红第 i 张牌上的数字是 𝑏 𝑖 ,具体的游戏过程如下:如果两人之中有一人已经没有牌了,则游戏结束。

比较两人当前手牌中的最前一张,对应数字大的那一方得一分并将该张牌从自己手牌移除;另一方不得分,手牌也不变。随后进入下一轮。

而现在小苯希望自己的得分尽可能多,为此他在游戏开始前可以任意地重新排列自己的牌,以得到更高的游戏分数。 现在你的任务则是求出,有多少种重新排列(选择不进行重排也是一种方案)的方式,能使得小苯得到他能得到的最高分。由于答案可能很大,请将答案对 998244353 取模后输出。

【名词解释】 长度为 𝑛 n 的排列:由 1,2,…,n 这 n 个整数、按任意顺序组成的数组(每个整数均恰好出现一次)。例如, {2,3,1,5,4} 是一个长度为 5 的排列,而 {1,2,2} 和 {1,3,4} 都不是排列,因为前者存在重复元素,后者包含了超出范围的数。

输入描述:

每个测试文件均包含多组测试数据。第一行输入一个整数 T(1≦T≦10 ^ 5 ) 代表数据组数,每组测试数据描述如下:

第一行输入一个正整数 n(1≦n≦2×10 ^
5 ),表示两人的卡牌数量。 第二行输入 n 个正整数 𝑎 1 , 𝑎 2 , … , 𝑎 𝑛 ( 1 ≦ 𝑎 𝑖 ≦ 2 × 𝑛 ),表示小苯的卡牌上的数字。 第三行输入 𝑛 n 个正整数 𝑏 1 , 𝑏 2 , … , 𝑏 𝑛 ( 1 ≦ 𝑏 𝑖 ≦ 2 × 𝑛 ),表示小红的卡牌上的数字。 (保证 a 数组和 b 数组共同构成一个长度为 2×n 的排列。)

除此之外,保证单个测试文件的 n 之和不超过 2×10 ^ 5 。

输出描述:

对于每一组测试数据,新起一行输出一个整数,表示小苯重新排列自己卡牌,使得他能得到的最高分的方案数对 998244353 取模后的值。

题解如下:

题目要求小苯需要获得最高分,这无疑是需要使数组a中的元素尽可能多的去除,因此,我们需要找到b数组中最小的数,由于b数组无法移动和改变,所以我们只需要计算a数组中大于b数组中mn值的数的个数,并计算此个数的全排列,再计算剩下数的个数的全排列,两者相乘取模即可,由于大于mn值的a数组中的元素放的靠前,所以在这些元素被移除之前,mn一直未被移除,所以可以保证得到最高分,而小于mn的元素无法得分,故两者分开全排列相乘取模即为答案。

代码附上:

#define endl '\n'
#define fs first
#define sc second
#define mod 998244353
#define N 10000000000
#define PI 3.141592653589793238462643383279L
using namespace std;
typedef long long int ll;
vector<ll>vt(201000);
ll A(ll a)
{
	return vt[a];
}
void solve()
{
	ll n,mn,cnt=0;
	cin>>n;
    mn=2*n+1;
	vector<ll>vc(n);
	for(ll i=0;i<n;i++)
	   cin>>vc[i];
	for(ll i=0;i<n;i++)
    {
        ll num;
        cin>>num;
        mn=min(mn,num);
    }
	for(ll i=0;i<n;i++)
	   if(vc[i]>mn)cnt++;
    if(cnt==0||cnt==n)cout<<A(n)<<endl;
    else 
	cout<<(A(cnt)*A(n-cnt))%mod<<endl;
}
int main()
{
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	ll _=1;
	vt[1]=1;
	for(ll i=2;i<201000;i++)
		vt[i]=(vt[i-1]%mod)*(i%mod)%mod;
	cin>>_;
	while(_--)
		solve();
	return 0;
}

如有错误烦请指正谢谢