原题:bzoj2873
100%测试点保证 n <= 200, m <= 1000000
建议先学preffer编码。
题解:dp套dp,ans[i]表示n取ans时的方案数,dp[i][j]表示i个点组成了j个环的时候的方案数,pow[i][j]表示i的j次方,C[i][j]表示组合数,huan[i]表示长度为i的环排列的方案数。解法:递推顺序十分恶心,尽管这题的细节并不多,相对于今天的t2要良心多了。。。
首先先求出pow,C,huan,没啥好说的,直接递推即可。
之后考虑dp数组的转移方式,因为我们不可能对于每个环都进行加边以及转移,所以只考虑在最后一个环中加边,答案没有影响(实际上就是只转移一次)
方程:dp[i][j]=(dp[i][j]+((dp[i-k][j-1]*k)%m)*(ans[k]*C[i-1][k-1])%m)%m;
意思就是,选择一个k,加入一个长度为k的环,这时因为缩点整个图之后要形成一棵树,所以*k,意思是枚举在哪里拆点,之后因为加入了k个点,所以乘以之前n=k的方案数,然后因为这k个点使我们随意选择的,所以乘上组合数。
ans数组的更新在注释中说的比较详细。
代码(bzoj AC):
#include
#define ll long long
using namespace std;
ll n,m,ans[205],dp[205][205],C[205][205],pow[205][205],huan[205];
int main()
{
scanf("%lld%lld",&n,&m);
ans[1]=ans[2]=dp[0][0]=dp[1][1]=dp[2][2]=huan[3]=1%m;
dp[2][1]=2%m;
for(int i=4;i<=n;i++)
huan[i]=(huan[i-1]*(i-1))%m;
for(int i=0;i<=n;i++)
{
C[i][0]=C[i][i]=1%m;
for(int j=1;j