(first)
看到数据范围应该都会多多少少有点思路了,要搞一个大概是(n^3)的(dp),可是貌似最多的方案数是(200!)级别的,计算器一算发现是(6e600+),于是要套高精。所以之前的(n^3)排除,将注意力放在(n^2-n^2logn),可(logn)这种奇怪的复杂度怎么会无缘无故出现在一个(n<=200)的题目?于是果断考虑(n^2dp)
(n^2)都知道什么意思吧,就是设置两个状态的(dp)方程
(second)
首先应当想到先排序,因为当身高有序后才能方便找出比某一女生矮的男生有多少,以方便进行(dp)计算
(third)
考虑设置(dp)方程(f_{i,j})表示当前遍历到第(i)矮的女生,我们已经匹配了(j)对,并且(j)对全是女大于男的方案数,这样的话(dp)方程可以比较快地得到
考虑将第(i)个女生与比第(i)个女生矮而不矮于第(i-1)个女生的一众男生(设人数为(p))一起加入(f_{i,j})的更新
那么(f_{i,j})首先肯定无条件继承(f_{i-1,j})的值,而当(p>j-1)时表明我们可以再已经有(j-1)对的基础上再扩展一对女大于男的舞伴,并且扩展的这一对有(p-(j-1))种方案,那么(f_{i,j})就需要加上(f_{i-1,j-1} * (p-j+1))
(forth)
但是(f_{n,j})还是不能帮我们解决问题,嗯
但已经接近尾声了,考虑设置(g_j)表示选完所有对,而其中至少有(j)对的方案数,那么显然(g_j=f_{n,j} * (n-j)!)(因为剩下(n-j)对没有要求,那么就是全排列嘛)
得到了(g_j)数组后就很容易想到容斥得到答案了(容斥系数自己可以瞎搞搞出来):
(fifth)
最后一点小注,本篇题解是对该题第一篇题解的一点补充,代码实现思路几乎相同。但由于彼题解有些许瑕疵(也可能是我太蒟蒻)导致没有怎么看懂,后来仔细推敲代码并手搞式子才有了以上这些想法,担心会有和我一样的小菜鸡(应该不会吧)对于首篇的做法有迷惑,于是作此题解来造(bao)福(fu)人(she)类(hui)
(sixth)
代码(代码中的(f_{i,j})是用的滚动数组,没算(g_j)而是直接代入(ans)公式,若还有不懂欢迎私信):
#include<bits/stdc++.h>
using namespace std;
#define ll long long
inline int read()
{
int x=0,f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar()) f=ch=='-'?-1:1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
return x*f;
}
const int xx=2e2+10,xd=100;
const int digit=1e9;
int m[xx],wm[xx],n,k;
struct Big
{
ll a[xd];
inline void print()
{
for(register int i=a[0];i;--i)
{
if(i!=a[0])
for(register int j=1e8;j>=10;j/=10)
a[i]<j?putchar('0'):j=1;
printf("%d",a[i]);
}
puts("");
}
}zero,one,fac[xx],f[2][xx],C[xx][xx];
Big ans;
Big operator * (Big x,Big y)
{
Big tmp=zero;tmp.a[0]=x.a[0]+y.a[0];
for(register int i=1;i<=x.a[0];++i)
for(register int j=1;j<=y.a[0];++j)
{
tmp.a[i+j-1]+=x.a[i]*y.a[j];
if(tmp.a[i+j-1]>=digit)
{
tmp.a[i+j]+=tmp.a[i+j-1]/digit;
tmp.a[i+j-1]%=digit;
}
}
while(!tmp.a[tmp.a[0]]&&tmp.a[0]>1)
--tmp.a[0];
return tmp;
}
Big operator * (int x,Big y)
{
Big tmp=zero;tmp.a[0]=y.a[0]+1;
for(register int i=1;i<=y.a[0];++i)
{
tmp.a[i]+=x*y.a[i];
if(tmp.a[i]>=digit)
{
tmp.a[i+1]+=tmp.a[i]/digit;
tmp.a[i]%=digit;
}
}
while(!tmp.a[tmp.a[0]]&&tmp.a[0]>1)
--tmp.a[0];
return tmp;
}
Big operator + (Big x,Big y)
{
Big tmp=zero;tmp.a[0]=max(x.a[0],y.a[0])+1;
for(register int i=1;i<=tmp.a[0]-1;++i)
{
tmp.a[i]+=(x.a[i]+y.a[i]);
if(tmp.a[i]>=digit)
{
tmp.a[i+1]+=tmp.a[i]/digit;
tmp.a[i]%=digit;
}
}
while(!tmp.a[tmp.a[0]]&&tmp.a[0]>1)
--tmp.a[0];
return tmp;
}
Big operator - (Big x,Big y)
{
Big tmp=zero;tmp.a[0]=x.a[0];
for(register int i=1;i<=x.a[0];++i)
{
tmp.a[i]=x.a[i]-y.a[i];
if(tmp.a[i]<0)
{
tmp.a[i]+=digit;
--x.a[i+1];
}
}
while(!tmp.a[tmp.a[0]]&&tmp.a[0]>1)
--tmp.a[0];
return tmp;
}
inline void pre()
{
memset(zero.a,0,sizeof(zero.a));
zero.a[0]=one.a[0]=one.a[1]=1;
for(register int i=0;i<=n;++i)
C[i][0]=one;
for(register int i=1;i<=n;++i)
for(register int j=1;j<=n;++j)
C[i][j]=C[i-1][j]+C[i-1][j-1];
for(register int i=0;i<=1;++i)
for(register int j=1;j<=n;++j)
f[i][j]=zero;
fac[0]=one;
for(register int i=1;i<=n;++i)
fac[i]=i*fac[i-1];
f[0][0]=f[1][0]=one;
}
int main()
{
n=read(),k=read(),pre();
for(register int i=1;i<=n;++i) m[i]=read();
for(register int i=1;i<=n;++i) wm[i]=read();
sort(m+1,m+n+1),sort(wm+1,wm+n+1);
for(register int i=1,K=0;i<=n;++i)
{
while(K<n&&m[K+1]<wm[i])
++K;
for(register int j=1;j<=i;++j)
{
if(K>j-1)
f[i&1][j]=(K-j+1)*f[(i-1)&1][j-1];
f[i&1][j]=f[i&1][j]+f[(i-1)&1][j];
}
}
ans=zero;
for(register int i=0;i<=k;++i)
for(register int j=i;j<=n;j+=2)
ans=ans+(f[n&1][j]*fac[n-j]*C[j][i]);
for(register int i=0;i<=k;++i)
for(register int j=i+1;j<=n;j+=2)
ans=ans-(f[n&1][j]*fac[n-j]*C[j][i]);
ans.print();
return 0;
}