【NOI2019模擬2019.6.27】幻化成風(集合容斥係數,胡亂dp)

Description:


在這裏插入圖片描述

題解:

xjb亂搞題,卡死在無序了。

考慮肯定是枚舉個集合劃分,然後強制一個集合裏的選的b一樣嘛,就可以無限揹包了,然後發現如果按題意說的無序的話特別難做,不妨考慮有序,即每一個a[i]都有標號,最後除以cnt[a[i]]\prod cnt[a[i]]就好了。

先思考暴力枚舉集合劃分,係數是什麼,係數應該是隻和這個集合包含的元素有關的,不妨設f[i]f[i]表示一個i個點的集合的容斥係數,顯然需要滿足:
[n=1]=ef[xn]n![n=1]=e^{f}[x^n]*n!
因爲是有標號的。
寫成dp式就是[n=1]=f[n]+i=1n1Cn1i1f[i][(ni)=1][n=1]=f[n]+\sum_{i=1}^{n-1}C_{n-1}^{i-1}*f[i]*[(n-i)=1]
歸納一下就是f[n]=(1)n1(n1)!f[n]=(-1)^{n-1}*(n-1)!

pty給了生成函數的推法:
要使efe^f次方的一次項係數爲1(常數),>1次項係數爲0,那麼直接將ln(1+x)ln(1+x)帶進去,經過一波求導、泰勒展開可以得到同樣的東西。

然後我們肯定不能暴力枚舉集合劃分。

注意對於一個劃分我們只關心每一個集合a[i]\sum a[i]和元素個素,那麼兩種劃分的各個集合的a[i]\sum a[i]和元素個素一樣的是可以縮到一起的,這個可以用set套map實現。

這樣直接dp還是會T非的,在搞完上面的之後,再把各個a[i]\sum a[i]一樣的縮到一起dp,由於30分整數拆分數只有5000+,即可通過本題……
.
.
.
纔怪,這個破題卡常到上天,

然後我發現前面的set套map巨慢,所以把set展開成數組,又搞了個hash值來快速比較。

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 N = 1e4 + 5;

int n, m, a[N], b[N];
int bz[N], p[N], c[N];

void sieve(int n) {
	fo(i, 2, n) {
		if(!bz[i]) p[++ p[0]] = i;
		for(int j = 1; i * p[j] <= n; j ++) {
			bz[i * p[j]] = 1;
			if(i % p[j] == 0) break;
		}
	}
	fo(i, 1, p[0]) {
		for(ll s = p[i]; s <= n; s *= p[i]) c[i] += n / s;
	}
}

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;
}
ll fac[N], nf[N];

struct P {
	int x, y;
	P(int _x = 0, int _y = 0) { x = _x, y = _y;}
};

bool operator < (P a, P b) {
	if(a.x == b.x) return a.y < b.y;
	return a.x < b.x;
}

#define ul unsigned long long
struct D {
	P a[31];
	ul s;
};
ul a3[105];
void calc(D &a) {
	a.s = 0;
	fo(i, 1, a.a[0].x)
		a.s += a3[i] * a.a[i].x + a3[i + 30] * a.a[i].y;
}

bool operator < (D a, D b) {
	return a.s < b.s;
	if(a.a[0].x != b.a[0].x) return a.a[0].x < b.a[0].x;
	if(a.a[1].x != b.a[1].x) return a.a[1].x < b.a[1].x;
	if(a.a[2].x != b.a[2].x) return a.a[2].x < b.a[2].x;
	fd(j, a.a[0].x, 3) {
		if(a.a[j].x < b.a[j].x) return 1;
		if(a.a[j].x > b.a[j].x) return 0;
		if(a.a[j].y < b.a[j].y) return 1;
		if(a.a[j].y > b.a[j].y) return 0;
	}
	return 0;
}

map<D, int> :: iterator it;
map<D, int> d[2]; int o;
#define fi first
#define se second

map<multiset<int>, int> t;
map<multiset<int>, int> :: iterator it3;
multiset<int> :: iterator it4;

int f[N];

ll ans;

D s;

int main() {
	freopen("count.in", "r", stdin);
	freopen("count.out", "w", stdout);
	a3[0] = 1; fo(i, 1, 100) a3[i] = a3[i - 1] * 7;
	scanf("%d %d", &n, &m);
	sieve(n);
	fo(i, 1, m) scanf("%d", &a[i]);
	sort(a + 1, a + m + 1);
	int m0 = m; m = 0;
	fo(i, 1, m0) {
		if(!m || a[i] != a[m])
			a[++ m] = a[i], b[m] = 1; else
			b[m] ++;
	}
	fac[0] = 1; fo(i, 1, 30) fac[i] = fac[i - 1] * i % mo;
	nf[30] = ksm(fac[30], mo - 2); fd(i, 30, 1) nf[i - 1] = nf[i] * i % mo;
	
	d[o][s] = 1;
	fo(i, 1, m) {
		fo(tim, 1, b[i]) {
			d[!o].clear();
			for(it = d[o].begin(); it != d[o].end(); it ++) {
				s = (*it).fi;
				fo(j, 1, s.a[0].x) {
					D g = s;
					g.a[j].x += a[i]; g.a[j].y ++;
					sort(g.a + 1, g.a + g.a[0].x + 1);
					calc(g);
					d[!o][g] = (d[!o][g] + (*it).se) % mo;
				}
				s.a[++ s.a[0].x].x = a[i]; s.a[s.a[0].x].y = 1;
				sort(s.a + 1, s.a + s.a[0].x + 1);
				calc(s);
				d[!o][s] = (d[!o][s] + (*it).se) % mo;
			}
			o = !o;
//		fprintf(stderr, "%d\n", d[o].size());
		}
	}
	for(it = d[o].begin(); it != d[o].end(); it ++) {
		s = (*it).fi; ll g = (*it).se;
		multiset<int> s2; s2.clear();
		fo(i, 1, s.a[0].x) {
			s2.insert(s.a[i].x);
			g = g * (mo - 1) % mo * fac[s.a[i].y - 1] % mo;
		}
		t[s2] = (t[s2] + g) % mo;
	}
	for(it3 = t.begin(); it3 != t.end(); it3 ++) {
		memset(f, 0, sizeof f);
		f[0] = 1;
		multiset<int> s = (*it3).fi;
		for(it4 = s.begin(); it4 != s.end(); it4 ++) {
			int x = (*it4);
			fo(j, x, c[1]) f[j] = (f[j] + f[j - x]) >= mo ? (f[j] + f[j - x] - mo) : (f[j] + f[j - x]);
		}
		ll sum = 1;
		fo(i, 1, p[0]) sum = sum * f[c[i]] % mo;
		ans = (ans + sum * (*it3).se) % mo;
	}
	fo(i, 1, m) ans = ans * nf[b[i]] % mo;
	if(m0 & 1) ans = (mo - ans) % mo;
	pp("%lld\n", ans);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章