『JROI-4』沈阳大街 2
艹!走!忽略!ጿ ኈ ቼ ዽ ጿ
算是比较巧妙的一题。
首先显然都可以爆搜加观察性质整到\(25pts\),然而对满分做法没什么引导。
首先本题转换为将A数组与B数组作一个匹配,在A数组和B数组之间连边从而覆盖全集。
然后考虑连边在整个答案当中的代价是连边两点的最小值,考虑去排序两个数组,然而这样仍然不好操作,于是我们可以考虑联合省选2021D1T1的套路将两个数组合并到一个数组中排序,相当于在\(2n\)个点中选取\(n\)对类型不同的点对,求代价和。
之后考虑在模拟过程中须知的量,发现需要可以往比自己小数连的点的点数,然后通过往回连这些点进行转移,然后对于往后连的点直接将代价乘当前点的点值即可,于是有一个dp状态\(f_{i,j,k}\)表示当前到\(i\)前面\(A\)类点未连接的有\(j\)个,\(B\)类点未连接的有\(k\)个到此的答案和,于是有\(n^3\)做法,结合特殊类的部分分有\(60pts\)。
之后考虑到只要确定了前面已经匹配的对数,便可以确定每个种类未连接的点数,于是设\(f_{i,j}\)为匹配到\(i\)前面有\(j\)对已经匹配,便可达到\(n^2\)复杂度。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=10010;const ll mod=998244353;
pair<int,int> a[N];long long f[10010][5010];int n,suma,sumb;
void add(ll &x,ll y){x=(x+y)%mod;}
ll qpow(ll x,ll y)
{
if(!y) return 1ll;
ll tmp=qpow(x,y/2);
if(y&1) return tmp*tmp%mod*x%mod;
return tmp*tmp%mod;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i].first),a[i].second=0;
for(int i=1;i<=n;i++)
scanf("%d",&a[i+n].first),a[i+n].second=1;
n*=2;sort(a+1,a+n+1);
f[0][0]=1;ll inv=1;
for(int i=2;i<=n/2;i++) inv=inv*i%mod;
for(int i=1;i<=n;i++)
{
suma+=a[i].second;sumb+=!a[i].second;
for(int j=0;j<=min(suma,sumb);j++)
if(a[i].second)
{
if(j) add(f[i][j],f[i-1][j-1]*(sumb-(j-1)));
add(f[i][j],f[i-1][j]*a[i].first);
}
else
{
if(j) add(f[i][j],1ll*f[i-1][j-1]*(suma-(j-1))%mod);
add(f[i][j],1ll*f[i-1][j]*a[i].first%mod);
}
}
printf("%lld",f[n][n/2]*qpow(inv,mod-2)%mod);
}