P4859 已经没有什么好害怕的了
最近在啃二项式反演,这道题就是一道很好的综合题,从0到最后完成,整个人的收获是非常多的。
首先整体思路没什么好说的:
- \(a\)至少比\(b\)大\(k\)对,也就是说:
1、\(a\)大于\(b\)的应该有(n + k) >> 1个
2、\((n + k) >> 1\)为偶数
- 先排序,定义一个\(cnt\)数组,\(cnt[i]\)数组代表,在\(i\)位置及以前,存在多少个\(b\)比\(a_i\)小。
- DP:求前\(n\)个人中恰好有\(i\)对组合,\(dp\)状态转移方程先根据实际意义推一下,\(dp[i][j]\)代表前\(a_i\)个恰好有\(j\)对的方案数有多少:我们可以推出来,\(dp[i][j]\)可以从\(dp[i - 1][j]\)转移过来,因为在\(i-1\)的位置已经满足了\(j\)对,所以在\(i\)位置就不做任何操作,另外,还可以从\(dp[i-1][j-1]\)的位置转移过来,在\(i-1\)位置是\(j-1\)对,在\(i\)位置如果存在\(cnt[i]-j+1\)对的话,那么\(dp[i-1][j-1]\)就可以为\(dp[i][j]\)贡献\(dp[i-1][j-1] \cdot (cnt[i]-j+1)\)多对(要减掉(\(j-1\))的)。
- 根据上面的\(dp[n][i]\)可以得到至少有多少对,也就是前面那个式子,敲好有\(i\)对的话,也就是前\(i\)个一定满足,后\(n-i\)个一定不满足,根据这里,得到至少有\(g[n]\)对,且\(g[k]=dp[n][k]*fac[n-k]\);
- 然后再定义一个变量\(ans\),它的实际含义就是最终答案,根据二项式反演可以得出
\[ans = \sum_{i=k}^n \cdot (-1)^{i-k} \cdot C_i^k \cdot g[i]
\]
到这里问题已经结束了
接下来就是代码
写这个博客目的并不是为了把整个题的思路都写下来,只是写一些让我觉得不错的题,或者说中间一些容易错的思路。
#include <bits/stdc++.h>
using namespace std;
#define mp make_pair
#define pb push_back
typedef long long ll;
const int N = 2e3 + 7, Mod = 1e9 + 9;
int a[N], b[N], n, k, cnt[N] = {0};
ll dp[N][N], f[N], frac[N], ret = 0, inv[N];
ll ksm(ll a, ll p){
ll ans = 1;
while (p){
if (p & 1) ans = (ans * a) % Mod;
a = (a * a) % Mod;
p >>= 1;
}
return ans;
}
void pre(){
int loc = 0, sz = 2e3;
for (int i = 1; i <= n; i++) {
while (loc < n && b[loc+1] < a[i]) ++loc;
cnt[i] = loc;
}
frac[1] = inv[1] = 1;
for (int i = 2; i <= sz; i++) frac[i] = (frac[i - 1] * i) % Mod;
for (int i = 1; i <= sz; i++) inv[i] = ksm(i, Mod - 2) % Mod;
}
ll C(int n, int m){
ll ans = 1;
for (int i = n, sz = n - m; i > sz; i--) ans = ans * i % Mod;
for (int i = 2; i <= m; i++) ans = ans * inv[i] % Mod;
return ans;
}
void solve(){
cin >> n >> k;
if ((n + k) & 1){
cout << "0" << endl;
return;
}
k = (n + k) >> 1;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) cin >> b[i];
sort(a + 1, a + 1 + n);
sort(b + 1, b + 1 + n);
pre();
dp[0][0] = 1;
for (int i = 1; i <= n; i++){
dp[i][0] = 1;
for (int j = 1; j <= cnt[i]; j++)
dp[i][j] = (dp[i - 1][j] + dp[i - 1][j - 1] * max(cnt[i] - j + 1, 0) % Mod) % Mod;
}
for (int i = 1; i <= n; i++) f[i] = dp[n][i] * frac[n - i] % Mod;
for (int i = k, flg = 1; i <= n; i++, flg = -flg)
ret = (ret + flg * C(i, k) * f[i] % Mod) % Mod;
cout << (ret % Mod + Mod) % Mod << endl;
}
int main(){
ios_base::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t = 1;
// cin >> t;
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号