题意:
有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=0∑n22n−iCnij∑iCijk∑jS(j,k)(2n−i)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=0∑n22n−iCnik∑i(2n−i)kj=k∑iCijS(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=k∑iCijS(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=0∑n22n−iCnik∑i(2n−i)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);
}