[ARC096C]Everything on It

题目

传送门 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 cks(1)ns(cn)(snc)(2s)k22s{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 nn0 的答案,因此还是 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,k1)+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;
}

你可能感兴趣的:(数学,#,容斥原理,卷爷天下第一,我不会做)