传送门:luoguP5241
题解
一波找规律尝试出 n 4 n^4 n4分做法,然而实际上是个贪心构造转DP:
考虑如何构造出一组合法序列:
首先让所有边首尾相连形成链,如果SCC数量产生变化,就缩链上开头的一部分。
在兼顾SCC数量变化的同时,其它边均贪心地用于串链,直到所有点都被串在了链上——这时就可以随便连了。
在没有将全部点串起来之前,设 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示已经串起了前 i i i个点,SCC数量变化了 j j j次,前 k k k个点缩在一起的方案数
在所有点串起来之后,设 g [ i ] [ j ] g[i][j] g[i][j]表示连了 i i i条边,前 j j j个点缩在一起的方案数。(注意 i ≤ ( n 2 ) + ( j 2 ) i\leq {n\choose 2}+{j\choose 2} i≤(2n)+(2j))
f , g f,g f,g转移均可以前缀和优化,复杂度 O ( n 3 ) O(n^3) O(n3)
代码
#include<bits/stdc++.h>
using namespace std;
const int N=405,mod=1e9+7;
int n,f[N][N],g[N*N][N],ans[N*N];
inline void ad(int &x,int y){x+=y;if(x>=mod) x-=mod;}
inline void dc(int &x,int y){x-=y;if(x<0) x+=mod;}
int main(){
int i,j,k,l,v,bs;f[0][1]=1;
scanf("%d",&n);bs=n*(n-1)>>1;
for(i=1;i<n;++i){
for(j=0;j<i;++j)
for(k=j+1;k<=i;++k)
ad(ans[i-1+j],f[j][k]);
if(i+1==n) break;
for(j=0;j<i;++j){
for(v=0,k=j+1;k<=i;++k)
{ad(f[j+1][k],v);ad(v,f[j][k]);}
ad(f[j+1][i+1],v);
}
}
for(j=0;j+1<n;++j)
for(k=j+1;k<n;++k)
ad(g[n-1+j][k],f[j][k]);
for(i=n-1;i<=n*(n-1);++i){
for(j=1;j<=n;++j) ad(ans[i],g[i][j]);
if(i==n*(n-1)) break;
for(j=1;j<=n;++j)
if(bs+(j*(j-1)/2)>i) ad(g[i+1][j],g[i][j]);
for(v=0,j=1;j<=n;++j){
ad(g[i+1][j],v);ad(v,g[i][j]);
}
}
for(i=1;i<=n*(n-1);++i){
printf("%d",ans[i]);
if(i<n*(n-1)) putchar(' ');
}
return 0;
}