题意
给你一个n,一共有2^n种组合,你要选择一些组合,使得每个数都出现至少两次,答案模m,m是一个质数,n<=5000
分析
这道题是计数题,很容易想到容斥
∑i=0n(−1)i22n−i(ni)∑j=0i(ij)∑k=1jS(j,k)(2n−i)k
∑
i
=
0
n
(
−
1
)
i
2
2
n
−
i
(
n
i
)
∑
j
=
0
i
(
i
j
)
∑
k
=
1
j
S
(
j
,
k
)
(
2
n
−
i
)
k
就是枚举有i个不合法,j个1,1分成k个集合
然后这样是
O(n3logm)
O
(
n
3
l
o
g
m
)
的,你可以拿到600分
考虑优化
∑i=0n(−1)i22n−i(ni)∑k=1i(2n−i)k∑j=ki(ij)S(j,k)
∑
i
=
0
n
(
−
1
)
i
2
2
n
−
i
(
n
i
)
∑
k
=
1
i
(
2
n
−
i
)
k
∑
j
=
k
i
(
i
j
)
S
(
j
,
k
)
因为
∑j=ki(ij)S(j,k)=S(i+1,k+1)
∑
j
=
k
i
(
i
j
)
S
(
j
,
k
)
=
S
(
i
+
1
,
k
+
1
)
这个式子可以反着理解,枚举多少个跟i+1一个集合
然后答案就是
∑i=0n(−1)i22n−i(ni)∑k=1i(2n−i)kS(i+1,k+1)
∑
i
=
0
n
(
−
1
)
i
2
2
n
−
i
(
n
i
)
∑
k
=
1
i
(
2
n
−
i
)
k
S
(
i
+
1
,
k
+
1
)
这样时间复杂度就是
O(n2logm)
O
(
n
2
l
o
g
m
)
代码
using namespace std;
typedef long long LL;
const LL N = 3010;
inline LL read()
{
LL p=0; LL f=1; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
LL C[N][N],S[N][N];
LL qpow(LL x,LL k,LL mo){LL s=1; while(k){if(k&1) s=s*x%mo; x=x*x%mo; k>>=1;} return s;}
int main()
{
LL n = read(); LL p = read();
C[0][0] = 1; for(LL i=1;i<=n+1;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<=n+1;i++)
{
S[i][1] = 1; for(LL j=2;j<=i;j++) S[i][j] = (S[i-1][j] * j + S[i-1][j-1]) % p;
}
LL ans = 0;
for(LL i=0;i<=n;i++)
{
LL s = C[n][i] * qpow(2,qpow(2,n-i,p-1),p) % p;
LL fr = 0; for(LL j=0;j<=i;j++) fr = (fr + qpow(2,(n-i)*j%(p-1),p) * S[i+1][j+1] % p) % p;
// printf("%lld %lld\n",s,fr);
ans =( (ans + ((i&1) ? (-1) : 1) * s % p * fr % p ) % p + p ) % p;
}
return printf("%lld\n",ans),0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)