[luogu P4859] 已经没有什么好害怕的了

我丢 : https://www.luogu.com.cn/problem/P4859

思路

这题的关键在
保证上面两行不会有重复的数字。
这句话
也就是说不存在相等的情况
那么问题转化为一一配对,使得恰好 ( n + k ) / 2 (n+k)/2 (n+k)/2 a i > b i a_i>b_i ai>bi

题解

恰好的不是很好求
但很明显钦定的很好求
所以可以直接用二项式反演

对于钦定的可以DP求出来
先把 a , b a,b a,b从小到大排序
设 f [ i ] [ j ] 表 示 前 i 个 , 有 j 个 a > b 的 情 况 , c n t [ i ] 表 示 有 c n t [ i ] 个 b 小 于 a [ i ] 设f[i][j]表示前i个,有j个a > b的情况,cnt[i]表示有cnt[i]个b小于a[i] f[i][j]ija>b,cnt[i]cnt[i]ba[i]
f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] ∗ ( c n t [ i ] − ( j − 1 ) ) f[i][j]=f[i-1][j-1]*(cnt[i]-(j-1)) f[i][j]=f[i1][j1](cnt[i](j1))

求完之后把剩下的随意配对 f [ n ] [ i ] = f [ n ] [ i ] ∗ ( n − i )   ! f[n][i]=f[n][i]*(n-i)~! f[n][i]=f[n][i](ni) !
然后二项式反演求出恰好的即可
code:

#include<bits/stdc++.h>
#define mod 1000000009
#define int long long
#define N 2005
using namespace std;
int a[N], b[N], c[N][N], cnt[N], f[N], fac[N], n, k;
signed main() {
	scanf("%lld%lld", &n, &k);
	fac[0] = 1;
	for(int i = 1; i <= n; i ++) fac[i] = fac[i - 1] * i % mod;
	for(int i = 0; i <= n; i ++) c[i][0] = 1;
	for(int i = 1; i <= n; i ++)
		for(int j = 1; j <= i; j ++)
			c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
	int m = (n + k) / 2;
	for(int i = 1; i <= n; i ++) scanf("%lld", &a[i]);
	for(int i = 1; i <= n; i ++) scanf("%lld", &b[i]);
	sort(a + 1, a + 1 + n), sort(b + 1, b + 1 + n);
	b[n + 1] = 2147483647;
	for(int i = 1; i <= n; i ++) cnt[i] = lower_bound(b + 1, b + 1 + n + 1, a[i]) - b - 1;
	
	f[0] = 1;
	for(int i = 1; i <= n; i ++) 
		for(int j = n; j >= 1; j --)
			f[j] += f[j - 1] * max(cnt[i] - (j - 1), 0ll) % mod, f[j] %= mod;
	int ans = 0;
	for(int i = m; i <= n; i ++)
		ans = (ans + ((i - m & 1)? -1 : 1) * c[i][m] % mod * f[i] % mod * fac[n - i] % mod + mod) % mod;
	printf("%lld", ans);
	return 0;
}
posted @ 2020-05-11 17:29  lahlah  阅读(18)  评论(0编辑  收藏  举报