洛谷P4707 重返現世(擴充套件min-max容斥+揹包dp+拆組合數)
阿新 • • 發佈:2020-07-30
https://www.luogu.com.cn/problem/P4707
題解:
擴充套件min-max容斥見:
https://www.cnblogs.com/coldchair/p/13404911.html
一開始使\(k=n-k+1\),意義轉為第\(k\)大。
然後套容斥:
\(\sum_{T} min(T)*\binom{|T|-1}{k-1}*(-1)^{|T|-k}\)
在此題中,很容易得到\(min(T)=\frac{m}{\sum{x \in T} ~ a[x]}\)
考慮一個dp,\(f[i][j][u]\)表示前\(i\)個確定了,\(|T|=j\),\(\sum{x \in T} ~ a[x]=u\)
\((-1)^{|T|-k}\)可以放進dp裡。
注意到\(k\le 10\),觀察\(\binom{|T|-1}{k-1}\),聯想到組合數性質:\(\binom{A+1}{B}= \binom{A}{B-1}+ \binom{A}{B}\)。
只要維護\(\binom{|T|-1}{0..k-1}\),當\(|T|++\)時,就可以推了。
Code:
#include<bits/stdc++.h> #define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++) #define ff(i, x, y) for(int i = x, _b = y; i < _b; i ++) #define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --) #define ll long long #define pp printf #define hh pp("\n") using namespace std; const int mo = 998244353; ll ksm(ll x, ll y) { ll s = 1; for(; y; y /= 2, x = x * x % mo) if(y & 1) s = s * x % mo; return s; } const int N = 10005; int n, k, m; int a[N]; ll fac[N], nf[N], inv[N]; void build(int n) { fac[0] = 1; fo(i, 1, n) fac[i] = fac[i - 1] * i % mo; nf[n] = ksm(fac[n], mo - 2); fd(i, n, 1) nf[i - 1] = nf[i] * i % mo; fo(i, 1, n) inv[i] = nf[i] * fac[i - 1] % mo; } ll C(int n, int m) { return n < m ? 0 : fac[n] * nf[m] % mo * nf[n - m] % mo; } ll f[2][11][N][2]; int o; void add(ll &x, ll y) { (x += y) %= mo;} int main() { build(10000); scanf("%d %d %d", &n, &k, &m); fo(i, 1, n) scanf("%d", &a[i]); k = n - k + 1; memset(f, 0, sizeof f); f[o][0][0][0] = (k % 2 ? -1 : 1); int sm = 0; fo(i, 1, n) { memset(f[!o], 0, sizeof f[!o]); fo(u, 0, sm) { fo(j, 0, k - 1) { add(f[!o][j][u + a[i]][1], -(f[o][j][u][1] + (j ? f[o][j - 1][u][1] : 0))); add(f[!o][j][u + a[i]][1], -f[o][j][u][0]); add(f[!o][j][u][0], f[o][j][u][0]); add(f[!o][j][u][1], f[o][j][u][1]); } } sm += a[i]; o = !o; } ll ans = 0; fo(u, 0, m) { ans = (ans + inv[u] * m % mo * f[o][k - 1][u][1]) % mo; } ans = (ans % mo + mo) % mo; pp("%lld\n", ans); }