CF888F Connecting Vertices

一、题目

点此看题

二、解法

一开始有一个很直接的想法,设 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 
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);
}

你可能感兴趣的:(区间dp)