• 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])个),划分数是(inom{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("
    ")
    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
    ", 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
    ", ans);
    }
    
  • 相关阅读:
    C# Lambda表达式
    C# LINQ用法
    C# XML介绍及基本操作
    C# 装箱和拆箱
    C# 堆与栈
    C#中ref和out的区别
    C#中16进制string字符串的转16byte互转
    C#中把一个Struct结构转换成Byte[]的方法
    SqlServer中查询操作记录的方法
    asp.net中后台获取Post参数(Json)最简单的一种方法。
  • 原文地址:https://www.cnblogs.com/coldchair/p/12912470.html
Copyright © 2020-2023  润新知