题目传送门
题解传送门
这题解最后一步似乎没讲清如何容斥的,或许是在大佬眼里,这过于显然(也可能是容斥的某个常用公式?)然而我并不知道,还是证一下吧。
对于有
i
个点的图,记至少有j个入度为0的结点的DAG数量是
并用
b[j]
表示恰好有j个入度为0的结点的DAG数量
那么,
f[i]=∑ij=0b[j]
考虑至少有j个入度为0的节点的DAG中统计进了k(
j≤k
)个节点,那么
a[j]=∑ik=j(kj)b[j]
所以,
∑ij=0(−1)j−1a[j]=∑ij=0(−1)j−1∑ik=j(kj)b[j]
把里层的和号提出来,就有
∑ij=0(−1)j−1∑ik=j(kj)b[j]=∑ij=1b[j]∑jk=1(jk)(−1)k−1
利用二项式定理,
(jk)(−1)k−1
等于一,然后就好了。
#include<cstdio>
const int N=3010,mo=1000000007;
int c[N][N],f[N],tw[N*N],i,j,n,x;
int main(){
scanf("%d",&n);
for(i=**c=1;i<=n;++i)
for(j=*c[i]=1;j<=i;++j)c[i][j]=(c[i-1][j-1]+c[i-1][j])%mo;
for(i=*f=*tw=1,j=n>>1;i<=j*(n-j);++i)tw[i]=(tw[i-1]<<1)%mo;
for(i=1;i<=n;++i)
for(j=x=1;j<=i;++j,x=-x)
f[i]=(f[i]+1ll*x*tw[j*(i-j)]*c[i][j]%mo*f[i-j]%mo+mo)%mo;
printf("%d\n",f[n]);
return 0;
}