【NOI2019模拟2019.6.27】幻化成风(集合容斥系数,胡乱dp)

Description:


【NOI2019模拟2019.6.27】幻化成风(集合容斥系数,胡乱dp)_第1张图片

题解:

xjb乱搞题,卡死在无序了。

考虑肯定是枚举个集合划分,然后强制一个集合里的选的b一样嘛,就可以无限背包了,然后发现如果按题意说的无序的话特别难做,不妨考虑有序,即每一个a[i]都有标号,最后除以 ∏ c n t [ a [ i ] ] \prod cnt[a[i]] cnt[a[i]]就好了。

先思考暴力枚举集合划分,系数是什么,系数应该是只和这个集合包含的元素有关的,不妨设 f [ i ] f[i] f[i]表示一个i个点的集合的容斥系数,显然需要满足:
[ n = 1 ] = e f [ x n ] ∗ n ! [n=1]=e^{f}[x^n]*n! [n=1]=ef[xn]n!
因为是有标号的。
写成dp式就是 [ n = 1 ] = f [ n ] + ∑ i = 1 n − 1 C n − 1 i − 1 ∗ f [ i ] ∗ [ ( n − i ) = 1 ] [n=1]=f[n]+\sum_{i=1}^{n-1}C_{n-1}^{i-1}*f[i]*[(n-i)=1] [n=1]=f[n]+i=1n1Cn1i1f[i][(ni)=1]
归纳一下就是 f [ n ] = ( − 1 ) n − 1 ∗ ( n − 1 ) ! f[n]=(-1)^{n-1}*(n-1)! f[n]=(1)n1(n1)!

pty给了生成函数的推法:
要使 e f e^f ef次方的一次项系数为1(常数),>1次项系数为0,那么直接将 l n ( 1 + x ) ln(1+x) ln(1+x)带进去,经过一波求导、泰勒展开可以得到同样的东西。

然后我们肯定不能暴力枚举集合划分。

注意对于一个划分我们只关心每一个集合 ∑ a [ i ] \sum a[i] a[i]和元素个素,那么两种划分的各个集合的 ∑ a [ i ] \sum a[i] a[i]和元素个素一样的是可以缩到一起的,这个可以用set套map实现。

这样直接dp还是会T非的,在搞完上面的之后,再把各个 ∑ a [ i ] \sum a[i] a[i]一样的缩到一起dp,由于30分整数拆分数只有5000+,即可通过本题……
.
.
.
才怪,这个破题卡常到上天,

然后我发现前面的set套map巨慢,所以把set展开成数组,又搞了个hash值来快速比较。

Code:

#include
#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);
}

你可能感兴趣的:(动态规划,容斥原理)