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);
}
posted @ 2020-05-18 19:04  Cold_Chair  阅读(221)  评论(0编辑  收藏  举报