一、题目
二、解法
一开始有一个很直接的想法,设 d p [ i ] [ j ] dp[i][j] dp[i][j]为建出区间 [ i , j ] [i,j] [i,j]的生成树的方案,转移:
- 选中间的一个点作为桥梁: ∑ d p [ i ] [ k ] × d p [ k ] [ j ] \sum dp[i][k]\times dp[k][j] ∑dp[i][k]×dp[k][j]
- 本来是两部分,连了 [ i , j ] [i,j] [i,j]之后合并了: ∑ d p [ i ] [ k ] × d p [ k + 1 ] [ j ] \sum dp[i][k]\times dp[k+1][j] ∑dp[i][k]×dp[k+1][j]
但是这种 d p dp dp处理不了 ( 1 , 2 ) ( 2 , 3 ) ( 3 , 4 ) (1,2)(2,3)(3,4) (1,2)(2,3)(3,4)这种连边情况,会算重。可以加一维, d p [ i ] [ j ] [ 0 / 1 ] dp[i][j][0/1] dp[i][j][0/1]表示 [ i , j ] [i,j] [i,j]之间是 / / /否强制连边,转移:
- d p [ i ] [ k ] [ 1 ] = ∑ d p [ i ] [ k ] [ 0 ] × ( d p [ k ] [ j ] [ 0 ] + d p [ k ] [ j ] [ 1 ] ) dp[i][k][1]=\sum dp[i][k][0]\times (dp[k][j][0]+dp[k][j][1]) dp[i][k][1]=∑dp[i][k][0]×(dp[k][j][0]+dp[k][j][1])
- d p [ i ] [ k ] [ 0 ] = ∑ ( d p [ i ] [ k ] [ 0 ] + d p [ i ] [ k ] [ 1 ] ) × ( d p [ k + 1 ] [ j ] [ 0 ] + d p [ k + 1 ] [ j ] [ 1 ] ) dp[i][k][0]=\sum (dp[i][k][0]+dp[i][k][1])\times (dp[k+1][j][0]+dp[k+1][j][1]) dp[i][k][0]=∑(dp[i][k][0]+dp[i][k][1])×(dp[k+1][j][0]+dp[k+1][j][1])
转移还有一些限制条件,需要注意一下。
#include <cstdio>
const int M = 505;
const int MOD = 1e9+7;
#define int long long
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,a[M][M],dp[M][M][2];
signed main()
{
n=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
a[i][j]=read();
for(int i=1;i<=n;i++)
dp[i][i][0]=1;
for(int i=n;i>=1;i--)
for(int j=i+1;j<=n;j++)
for(int p=i;p<=j;p++)
{
if(a[i][j] && p<j)
{
dp[i][j][0]+=(dp[i][p][0]+dp[i][p][1])*(dp[p+1][j][0]+dp[p+1][j][1])%MOD;
dp[i][j][0]%=MOD;
}
if(p>i && p<j)
{
dp[i][j][1]+=dp[i][p][0]*(dp[p][j][0]+dp[p][j][1])%MOD;
dp[i][j][1]%=MOD;
}
}
printf("%lld\n",(dp[1][n][0]+dp[1][n][1])%MOD);
}