题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3622
令 f[i] 表示钦定 i 对 a[ ]>b[ ] 的关系的方案数;g[i] 表示恰好 i 对 a[ ]>b[ ] 的关系的方案数。
那么 (f[i]=sumlimits_{j>=i}C_{j}^{i}*g[j] ) ,(g[i]=sumlimits_{j>=i}C_{j}^{i}f[j](-1)^{j-i} )
考虑怎么求 f[ ] 。可以 DP 。
先把 a[ ] 和 b[ ] 都按从小到大的顺序排序,dp[i][j]表示前 i 个 a[ ] 匹配了 j 对 a[ ] > b[ ] 的关系的方案数。
排序的好处就是 a[ ] > b[ ] 的一段 b[ ] ,a[i] 的这一段能包含 a[i-1] 的这一段。所以转移就是 dp[i][j]=dp[i-1][j]+dp[i-1][j-1]*(p0-(j-1)),其中p0是比 a[i] 小的 b[ ] 的个数。
然后别忘了 f[ i ] = dp[n][i]*(n-i)! ,阶乘表示其他配对可以随意。
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; int Mn(int a,int b){return a<b?a:b;} const int N=2005,mod=1e9+9; int upt(int x){if(x>=mod)x-=mod;return x;} int n,k,a[N],b[N],dp[N],c[N][N]; void init() { for(int i=0;i<=n;i++)c[i][0]=1; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) c[i][j]=upt(c[i-1][j]+c[i-1][j-1]); } int main() { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++)scanf("%d",&a[i]); for(int i=1;i<=n;i++)scanf("%d",&b[i]); k+=n;if(k&1){puts("0");return 0;}// k>>=1; sort(a+1,a+n+1); sort(b+1,b+n+1); int p0=0;dp[0]=1; for(int i=1;i<=n;i++) { while(p0<n&&b[p0+1]<a[i])p0++; for(int j=Mn(i,p0);j;j--) dp[j]=(dp[j]+(ll)dp[j-1]*(p0-j+1))%mod; } for(int i=n,lj=1,j=0;i;i--,j++,lj=(ll)lj*j%mod) dp[i]=(ll)dp[i]*lj%mod; int ans=0; init(); for(int i=k,j=1;i<=n;i++,j=-j) ans=(ans+(ll)dp[i]*j*c[i][k])%mod; if(ans<0)ans+=mod; printf("%d ",ans); return 0; }