传送门 to AtCoder
我 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 // Almighty OUYE yyds!!!
#include
#include
#include // 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;
}