洛谷P4707 重返现世(扩展min-max容斥+背包dp+拆组合数)
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\)的方案数,可以获得70p。
\((-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);
}
转载注意标注出处:
转自Cold_Chair的博客+原博客地址