AtCoder Regular Contest 096 E

题意:

有n个数, 2 n 2^n 2n个集合,从中选择一些集合,问有多少种方案使每个数至少出现两次。

题解:

直接计数不好计,考虑容斥,枚举不合法。
a n s = ∑ i = 0 n 2 2 n − i C n i ∑ j i C i j ∑ k j S ( j , k ) ( 2 n − i ) k ans=\sum_{i=0}^n2^{2^{n-i}}C_n^i\sum_j^iC_i^j\sum_k^jS(j,k)(2^{n-i})^k ans=i=0n22niCnijiCijkjS(j,k)(2ni)k
S ( n , m ) S(n,m) S(n,m)是第二类斯特林数, j j j为枚举有多少个数只出现一次,再将他们分到若干集合中。
考虑优化:
a n s = ∑ i = 0 n 2 2 n − i C n i ∑ k i ( 2 n − i ) k ∑ j = k i C i j S ( j , k ) ans=\sum_{i=0}^n2^{2^{n-i}}C_n^i\sum_k^i(2^{n-i})^k\sum_{j=k}^iC_i^jS(j,k) ans=i=0n22niCniki(2ni)kj=kiCijS(j,k)

我们有:
∑ j = k i C i j S ( j , k ) = S ( i + 1 , k + 1 ) \sum_{j=k}^iC_i^jS(j,k)=S(i+1,k+1) j=kiCijS(j,k)=S(i+1,k+1)

这个很好理解,相当于枚举有多少不和 i + 1 i+1 i+1一个集合。
a n s = ∑ i = 0 n 2 2 n − i C n i ∑ k i ( 2 n − i ) k S ( i + 1 k + 1 ) ans=\sum_{i=0}^n2^{2^{n-i}}C_n^i\sum_k^i(2^{n-i})^kS(i+1k+1) ans=i=0n22niCniki(2ni)kS(i+1k+1)

O ( n 2 l o g m ) O(n^2logm) O(n2logm)

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#define LL long long
using namespace std;
LL C[3010][3010],S[3010][3010];
LL n,p;
void pre()
{
	for(LL i=0;i<=3001;i++)
	{
		C[i][0]=1;
		for(LL j=1;j<=i;j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%p;
	}
	S[0][0]=1;
	for(LL i=1;i<=3001;i++)
	{
		S[i][1]=1;
		for(LL j=2;j<=i;j++) S[i][j]=(S[i-1][j]*j%p+S[i-1][j-1])%p;
	}
}
LL pow(LL a,LL b,LL mod)
{
	LL ans=1;
	while(b)
	{
		if(b&1) ans=ans*a%mod;
		a=a*a%mod;b>>=1;
	}
	return ans;
}
int main()
{
	LL ans=0;scanf("%lld %lld",&n,&p);
	pre();
	for(LL i=0;i<=n;i++)
	{
		LL s=C[n][i]*pow(2,pow(2,n-i,p-1),p)%p;
		LL tot=0;
		for(LL j=0;j<=i;j++) tot=(tot+pow(2,(n-i)*j%(p-1),p)*S[i+1][j+1]%p)%p;
		ans=(ans+(i&1?-1:1)*s%p*tot%p)%p;
	}
	printf("%lld",(ans+p)%p);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值