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

Description:


在这里插入图片描述

题解:

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<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);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
信息学奥赛是一项重要的竞赛活动,旨在培养学生的计算机科学和信息技术能力。省选是指在全省范围内举办的预赛,参赛选手需要通过省选才能晋级到更高层次的比赛。在省选中发挥出色的选手将有机会代表本省参加全国信息学奥林匹克竞赛(NOI)。 而noi_pdf-2020.12.29.rar 是一个压缩文件的命名,其中包含了一份关于NOI的PDF文档。这个文件可能包含了有关NOI的相关资料,例如竞赛规则、题目类型、考试要求等等。通过研究这个文件,选手可以更好地准备信息学奥赛,提高竞赛成绩。 对于想要参加信息学奥赛的同学们来说,可以利用这份PDF文档来深入了解NOI的要求和考试内容。首先,可以仔细阅读竞赛规则,了解比赛的时间、地点、参赛资格等重要信息。其次,可以通过研究题目类型和考试要求,明确自己需要学习和复习的内容,制定合理的备考计划。此外,可以通过查阅往年的竞赛题目和解答,进行练习和模拟考试,提高解题能力和应变能力。 综上所述,信息学奥赛省选和noi_pdf-2020.12.29.rar对于想要参加信息学奥赛的同学们来说都有重要的意义。省选是选拔出优秀选手的一个重要阶段,而noi_pdf-2020.12.29.rar则提供了有关NOI的重要资料,帮助选手更好地准备竞赛。希望通过努力学习和准备,同学们可以在比赛中取得优异成绩,提升自己的计算机科学和信息技术能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值