JZOJ 5728. 简单计数|| (容斥+动态规划)
Description:
https://gmoj.net/senior/#main/show/5728
题解:
考虑不是环上怎么做:
预处理\(f[i][j]\)表示i个分成j段,段与段之间有序,且乘上的了段的大小,这样的所有方案权值和。
考虑,现在相当于有第i个颜色有\(b[i]\)段,把这些合并,是的没有相邻两段颜色相同。
再容斥,枚举\(c[i]\)第i个颜色实际上是有\(c[i]\)段,容斥系数是\((-1)^{b[i]-c[i]}\)(本来要求零个相同相同,现在有\(b[i]-c[i]\)个),划分数是\(\binom{b[i]-1}{c[i]-1}\)
再把这些\(c[i]\)的OGF卷起来即可。
考虑环上,使颜色\(1\)为开头一段,且不为结尾一段,然后可以旋转,也就是乘上\(n/1的段数\),发现刚好不会算重。
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 = 1e9 + 7;
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 = 105;
int n, a[N];
ll fac[N * N], nf[N * N], inv[N * 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) {
if(n < m) return 0;
return fac[n] * nf[n - m] % mo * nf[m] % mo;
}
ll f[N][N];
void add(ll &x, ll y) { (x += y) %= mo;}
void build2(int n) {
f[0][0] = 1;
fo(i, 1, n) {
fd(j, n, 0) fo(k, 0, j) if(f[j][k]) {
ll s = 1;
fo(u, 1, (n - j) / i) {
s = s * i % mo;
add(f[j + i * u][k + u], f[j][k] * s % mo * nf[u]);
}
}
}
fo(i, 0, n) fo(j, 0, i) f[i][j] = f[i][j] * fac[j] % mo;
}
ll g[N * N], h[N], g0[N * N];
void mer() {
fo(i, 0, 1e4) g0[i] = g[i], g[i] = 0;
fo(i, 0, 1e4) if(g0[i]) fo(j, 0, 100) if(h[j]) {
add(g[i + j], g0[i] * h[j]);
}
}
int main() {
freopen("number.in", "r", stdin);
freopen("number.out", "w", stdout);
build(1e4);
scanf("%d", &n);
int sa = 0;
fo(i, 1, n) scanf("%d", &a[i]), sa += a[i];
if(n == 1) {
pp("%d\n", a[1]);
return 0;
}
build2(100);
g[0] = 1;
fo(i, 1, n) {
fo(j, 0, 100) h[j] = 0;
fo(j, 1, a[i]) {
ll xs = f[a[i]][j];
fo(k, 1, j) {
ll v = xs * C(j - 1, k - 1) % mo * ((k - j) % 2 ? -1 : 1);
if(i > 1) {
add(h[k], v * nf[k]);
} else {
add(h[k - 1], v * nf[k - 1] % mo * inv[j] % mo * sa);
if(k > 1) add(h[k - 2], -v * nf[k - 2] % mo * inv[j] % mo * sa);
}
}
}
mer();
}
ll ans = 0;
fo(i, 1, 1e4) ans = (ans + g[i] * fac[i]) % mo;
ans = (ans % mo + mo) % mo;
pp("%lld\n", ans);
}
转载注意标注出处:
转自Cold_Chair的博客+原博客地址