codeforces 1437 F - Emotional Fishermen (計數dp)
阿新 • • 發佈:2020-12-04
題目連結:https://codeforces.com/contest/1437/problem/F
一個很重要的性質:happy 的漁夫手中的魚的重量一定是遞增的,且每個 happy 漁夫的魚都比前一個 happy 的漁夫重至少兩倍
於是我們就可以列舉 happy 的漁夫,剩下的漁夫肯定都是 sad 的
首先將 \(a\) 排序,\(dp[i]\) 表示第 \(i\) 個漁夫是 happy 的方案數,
轉移的時候,列舉前一個 happy 的漁夫 \(j\)
注意,前一個漁夫的 \(a[j] <= \frac{a[i]}{2}\), 所以設 \(px[i]\) 為滿足 \(a[x] <= \frac{a[i]}{2}\)
所以 \(j\) 只需要列舉到 \(px[i]\) 即可
同時,\(px[i]\) 以內的所有不是 happy 的漁夫肯定都是 sad 的,現在來考慮安排這些漁夫位置的方案數
當前已經安排好了 \(i, j, px[j]\) 這些漁夫,所以還剩餘 \(n - 2 - px[j]\) 個空位,還有 \(px[i] - px[j] - 1\) 個漁夫需要安排(-1 是因為 \(j\) 在 \(px[i]\) 中),剩下的漁夫要安排在漁夫 \(i\) 的後面才能保證他們是 sad,而後來的 happy 的漁夫肯定比當前 \(i\) 漁夫的 \(a\) 要大,所以這些 sad 的漁夫安排在後面的任意位置都可以,而漁夫 \(i\)
\(i\) 不 happy
\(dp\) 轉移方程為:
$$dp[i] = \sum_{0}^{px[i]}A(n - 2 - px[j], px[i] - px[j] - 1)$$
最後要特別注意,因為最大的漁夫必須是 happy 的,所以第 \(n - 1\) 小的漁夫一定要滿足 \(a[n - 1] <= \frac{a[n]}{2}\), 也就是 \(px[n] = n - 1\)
否則的話方案數一定為 \(0\)
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> #include<cmath> using namespace std; typedef long long ll; const int maxn = 5010; const int M = 998244353; int n, m; int a[maxn], dp[maxn], fac[maxn], ifac[maxn], px[maxn]; int sta[maxn], p[maxn], tail = 0; int qsm(int i, int po){ int res = 1; while(po){ if(po & 1) res = 1ll * res * i % M; po >>= 1; i = 1ll * i * i % M; } return res; } int A(int n, int m){ if(m > n) return 0; return 1ll * fac[n] * ifac[n - m] % M; } ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; } int main(){ fac[0] = 1; for(int i = 1 ; i <= 5000 ; ++i) fac[i] = 1ll * fac[i - 1] * i % M; ifac[5000] = qsm(fac[5000], M - 2); for(int i = 4999 ; i >= 0 ; --i) ifac[i] = 1ll * ifac[i + 1] * (i + 1) % M; n = read(); for(int i = 1 ; i <= n ; ++i) a[i] = read(); sort(a + 1, a + 1 + n); int top = 0; for(int i = 1 ; i <= n ; ++i){ while(a[top + 1] * 2 <= a[i]) ++top; px[i] = top; } dp[0] = 1, px[0] = -1; for(int i = 1 ; i <= n ; ++i){ for(int j = 0 ; j <= px[i] ; ++j){ dp[i] = (dp[i] + 1ll * dp[j] * A(n - 2 - px[j], px[i] - px[j] - 1) % M) % M; } } if(px[n] == n - 1) printf("%d\n", dp[n]); // 最大的數必須開心 else printf("0\n"); return 0; }