大致题意: 求(n)个点的有标号(DAG)数目。
前言
不会数数的我实在太菜太菜。。。
(DP)
考虑(DAG)中必然存在入度为(0)的点,且去掉这些入度为(0)的点之后得到的依然是(DAG)。
于是,我们可以设(f_i)表示(i)个点的有标号(DAG)数目,枚举一个(j)表示至少存在(j)个入度为(0)的点。
显然有此时的方案数为:
[C_i^j imes 2^{j imes (i-j)} imes f_{i-j}
]
即,枚举选出哪(j)个点,枚举入度为(0)的点和其他点之间的边是否选择,而其他点依然形成(DAG)可以直接从已有状态转移。
看到至少,我们可以考虑容斥。
因此得到最终的转移方程:
[f_i=sum_{j=1}^i(-1)^{j-1} imes C_i^j imes2^{j imes(i-j)} imes f_{i-j}
]
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 3000
#define X 1000000007
using namespace std;
int n,f[N+5],pw[N*N+5],C[N+5][N+5];
int main()
{
RI i,j,op;for(scanf("%d",&n),pw[0]=i=1;i<=n*n;++i) (pw[i]=pw[i-1]<<1)>=X&&(pw[i]-=X);//预处理2的幂
for(C[0][0]=i=1;i<=n;++i) for(C[i][0]=j=1;j<=i;++j) C[i][j]=(C[i-1][j-1]+C[i-1][j])%X;//预处理组合数
for(f[0]=i=1;i<=n;++i) for(op=j=1;j<=i;op=X-op,++j)//枚举j
f[i]=(1LL*op*C[i][j]%X*pw[j*(i-j)]%X*f[i-j]+f[i])%X;//容斥转移
return printf("%d",f[n]),0;
}