正题
题目链接:https://www.luogu.com.cn/problem/AT4119
题目大意
一个集合S={k∈[1,n]∪N}S=\{k\in[1,n]\cup N\}S={k∈[1,n]∪N},它的所有子集作为元素组成的集合中要求满足每一个数字的出现之和不小于222,求方案数对PPP取模。
1≤n≤3000,P∈[108,109+9]∪Pri1\leq n\leq 3000,P\in[10^8,10^{9}+9]\cup Pri1≤n≤3000,P∈[108,109+9]∪Pri
解题思路
考虑至少iii个数选择次数不超过111,那么这个方案的容斥系数就是(−1)i(-1)^i(−1)i。
考虑怎么求这个方案,我们可以先不要被限制了的数,然后再将这些被限制了的数丢进被选出了的集合中。设有jjj个集合包含被限制了的数,那么丢进这些集合的方案就是{i+1j+1}\begin{Bmatrix} i+1\\j+1 \end{Bmatrix}{i+1j+1}(一个数字可以选择不丢所以开一个新的集合表示这个集合内的数不使用),然后剩下的数随意的选入这些集合中就是(2n−i)j(2^{n-i})^j(2n−i)j。
那么答案出来了
∑i=0n(−1)i22n−i(ni)∑j=0i{i+1j+1}(2n−i)j\sum_{i=0}^n(-1)^i2^{2^{n-i}}\binom{n}{i}\sum_{j=0}^i\begin{Bmatrix}i+1\\ j+1\end{Bmatrix}(2^{n-i})^ji=0∑n(−1)i22n−i(in)j=0∑i{i+1j+1}(2n−i)j
直接预处理斯特林数计算就好了,时间复杂度O(n2)O(n^2)O(n2)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=3100;
ll n,P,s[N][N],fac[N],ans;
ll power(ll x,ll b,ll p=P){ll ans=1;while(b){if(b&1)ans=ans*x%p;x=x*x%p;b>>=1;}return ans;
}
ll C(ll n,ll m)
{return fac[n]*power(fac[m],P-2)%P*power(fac[n-m],P-2)%P;}
signed main()
{scanf("%lld%lld",&n,&P);s[0][0]=fac[0]=1;for(ll i=1;i<=n;i++)fac[i]=fac[i-1]*i%P;for(ll i=1;i<=n+1;i++)for(ll j=1;j<=i;j++)s[i][j]=(s[i-1][j-1]+j*s[i-1][j]%P)%P;for(ll i=0;i<=n;i++){ll sum=0,tmp=power(2,power(2,n-i,P-1));if(i&1)tmp=P-tmp;tmp=tmp*C(n,i)%P;for(ll j=0,z=1,p=power(2,n-i);j<=i;j++,z=z*p%P)(sum+=s[i+1][j+1]*z%P)%=P;(ans+=sum*tmp)%=P;}printf("%lld\n",ans);return 0;
}