题目
思路
我 t m \rm tm tm 真的郁闷啊。
d
p
\tt dp
dp 不易,考虑容斥。我一心想着钦定
=
0
=0
=0 和
=
1
=1
=1 的元素,于是答案就是
∑
c
∑
k
∑
s
(
−
1
)
n
−
s
(
n
c
)
(
n
−
c
s
)
(
2
s
)
k
⋅
2
2
s
{
c
k
}
\sum_{c}\sum_{k}\sum_{s}(-1)^{n-s}{n\choose c}{n-c\choose s}(2^s)^k\cdot 2^{2^s}\left\lbrace\genfrac{}{}{0pt}{}{c}{k}\right\rbrace
c∑k∑s∑(−1)n−s(cn)(sn−c)(2s)k⋅22s{kc}
其中 c c c 枚举了 = 1 =1 =1 的元素的数量, s s s 枚举了无限制的元素的数量,第二类斯特林数 { c k } \left\lbrace\genfrac{}{}{0pt}{}{c}{k}\right\rbrace {kc} 表示将 c c c 个元素划分到 k k k 个无标号非空集合中的方案数。然后我就傻眼了,能想到的最好的是 O ( n 2 log n ) \mathcal O(n^2\log n) O(n2logn) 基于卷积的做法 😢
然后我又试着先除去含 = 1 =1 =1 元素的方案,然后除去含 = 0 =0 =0 元素的方案。这需要求出所有 n ⩽ n 0 n\leqslant n_0 n⩽n0 的答案,因此还是 O ( n 3 ) \mathcal O(n^3) O(n3) 的。
好吧,滚去看题解。一句话总结就是:将 { c k } \left\lbrace\genfrac{}{}{0pt}{}{c}{k}\right\rbrace {kc} 视为函数 f ( c , k ) f(c,k) f(c,k) 不可能经过数学变换找到结果——枚举 k , s k,s k,s 之后,对 c c c 的求和可以快速求出,因为存在组合意义!
记
f
(
c
,
k
)
f(c,k)
f(c,k) 为,选出
[
1
,
c
]
∩
Z
[1,c]\cap\Z
[1,c]∩Z 的大小为
k
k
k 的子集族,使得每个元素出现次数
⩽
1
\leqslant 1
⩽1 的方案数。事实上很容易
d
p
\tt dp
dp,与第二类斯特林数方法相同:
f
(
c
+
1
,
k
)
=
f
(
c
,
k
−
1
)
+
f
(
c
,
k
)
×
k
+
f
(
c
,
k
)
f(c{+}1,k)=f(c,k{-1})+f(c,k)\times k+f(c,k)
f(c+1,k)=f(c,k−1)+f(c,k)×k+f(c,k)
最后一项就是出现次数 = 0 =0 =0 的情况。然后按照之前的式子计算即可做到 O ( n 2 ) \mathcal O(n^2) O(n2) 。
另:细看递推式,好像我们会发现
f
(
c
,
k
)
=
(
k
+
1
)
{
c
k
+
1
}
+
{
c
k
}
f(c,k)=(k{+}1)\left\lbrace\genfrac{}{}{0pt}{}{c}{k+1}\right\rbrace+\left\lbrace\genfrac{}{}{0pt}{}{c}{k}\right\rbrace
f(c,k)=(k+1){k+1c}+{kc}
你便想到一个构造性的解释:就是让出现次数 = 0 =0 =0 的元素构成一个集合呗!
代码
#include <cstdio> // Almighty OUYE yyds!!!
#include <algorithm>
#include <cstring>
#include <cctype> // isdigit
using llong = long long;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
# define rep0(i,a,b) for(int i=(a); i!=(b); ++i)
inline int readint(){
int a = 0, c = getchar(), f = 1;
for(; !isdigit(c); c=getchar()) if(c == '-') f = -f;
for(; isdigit(c); c=getchar()) a = a*10+(c^48);
return a*f;
}
const int MAXN = 3000;
int f[MAXN+1][MAXN+1], comb[MAXN+1][MAXN+1];
inline llong qkpow(llong b, int q, int mod){
llong a = 1;
for(; q; q>>=1,b=b*b%mod) if(q&1) a = a*b%mod;
return a;
}
int main(){
int n = readint(), mod = readint();
rep(i,f[0][0]=1,n) rep(j,f[i][0]=1,n)
f[i][j] = int((f[i-1][j-1]+llong(j+1)*f[i-1][j])%mod);
rep(i,comb[0][0]=1,n) rep(j,comb[i][0]=1,i)
comb[i][j] = (comb[i-1][j-1]+comb[i-1][j])%mod;
int ans = 0;
for(int s=0,v=0; s<=n; ++s,v=0){
llong unit = qkpow(2,s,mod), pv = 1;
rep(i,0,n){
v = int((v+pv*f[n-s][i])%mod);
pv = pv*unit%mod;
}
llong coe = qkpow(2,int(qkpow(2,s,mod-1)),mod);
v = int(coe*v%mod*comb[n][s]%mod);
if((n^s)&1) ans = (ans+mod-v)%mod;
else ans = (ans+v)%mod;
}
printf("%d\n",ans);
return 0;
}