ACG054 B - Greedy Division
阿新 • • 發佈:2021-06-29
目錄
\[ans=\sum^n_{i=1}f_i\cdot i!\cdot (n-i)!
\]
題目
https://atcoder.jp/contests/agc054/tasks/agc054_b
思路
怎麼看,這題應該要DP,但是描述一個狀態是很大問題:你怎麼知道選了哪些橙子(共有\(2^{100}\)種情況)?
經過一番漫長的思考後,師父,我悟了!
其實,並不用描述選了哪些橙子,題目的關鍵就在排列\(P\)和那兩人拿橘子的順序是一一對應的,比如說,Takahashi 依次取了1,5,2號橙子,Aoki依次取了4,3號橙子,那麼,有且僅有一個\(P\)和這種情況對應,且該\(P\)只對應這種情況(用心體會下)
這樣,問題就簡單多了,設\(f_i\)表示拿了\(i\)個橙子,且它們的重量和恰好等於所有橙子重量和的一半,的方案數.答案就應該是(別忘了取模):
\(i!\)是其中一個人拿橙子的順序的方案數,\((n-i)!\)就是另一個人拿橙子的順序的方案數
如何得到\(f\)?
DP:\(f_{i,j}\)表示拿了\(i\)個橙子,總重量為\(j\)的方案數,過程有點像揹包問題,應該都會了吧
程式碼
#include <iostream> #include <cstdio> using namespace std; #define N 110 #define ll long long #define mod 998244353ll ll f[N][10010]; ll n , w[N] , sumw; ll fac[N]; int main() { fac[0] = 1; for(int i = 1 ; i <= 100 ; i++) fac[i] = fac[i - 1] * i % mod; cin >> n; for(int i = 1 ; i <= n ; i++) { cin >> w[i]; sumw += w[i]; } if(sumw % 2 == 1) { cout << 0; return 0; } f[0][0] = 1; for(int i = 1 ; i <= n ; i++)//列舉第i個橙子 for(int j = n ; j >= 0 ; j--)//一共j個橙子(除了i),注意倒敘列舉 for(int k = sumw ; k >= 0 ; k--)//重量(除了i) f[j + 1][k + w[i]] += f[j][k], f[j + 1][k + w[i]] %= mod; ll ans = 0; for(int i = 1 ; i <= n ; i++)//求答案 ans = (ans + fac[i] * fac[n - i] % mod * f[i][sumw / 2] % mod) % mod; cout << ans << endl; return 0; }