题目
题意
并不难懂,看洛谷上题面即可。
思路
在做过之前几道区间 d p \tt dp dp后,这题一开始还是有些思路的。然而打完之后才发现:依照我的状态枚举,会有重复的状态——难不成,得容斥?瞥了一眼旁边的 F r e n c h \tt French French 痴傻的眼,我放弃了容斥的想法。正当我举棋不定时,一缕曙光照了进来——旁边的 JZM 巨佬告诉我:可以加一维 01 01 01 进行判断!我恍然大雾!
再看了一眼题解,才悟到可以将此问题转化为括号问题。进一步进行简化。
于是,得到如下的区间 d p \tt dp dp :
-
d p [ l ] [ r ] [ 0 ] dp[l][r][0] dp[l][r][0] : 表示不连 l − r l-r l−r,在分出的左区间和右区间分别连边的方案数。
-
d p [ l ] [ r ] [ 1 ] dp[l][r][1] dp[l][r][1] : 表示连 l − r l-r l−r,在分出的左区间和右区间分别连边的方案数。
然而刚才准备考虑转移时,又卡住了。这时,我们就得借画图来进行理解:
- d p [ l ] [ r ] [ 0 ] dp[l][r][0] dp[l][r][0] 的转移:
考虑枚举断点 m i d mid mid,由于整体的考虑性,我们认为 l − m i d l-mid l−mid 是 l l l 连接的 m i d mid mid 及右边唯一的一个点。那么这时,左边的方案数为 d p [ l ] [ m i d ] [ 1 ] dp[l][mid][1] dp[l][mid][1]。而右边方案数为 d p [ m i d ] [ r ] [ 0 / 1 ] dp[mid][r][0/1] dp[mid][r][0/1] 显然,因此得到:
d p [ l ] [ r ] [ 0 ] = ∑ m i d = l r − 1 d p [ l ] [ m i d ] [ 1 ] ∗ ( d p [ m i d ] [ r ] [ 0 ] + d p [ m i d ] [ r ] [ 1 ] ) dp[l][r][0]=\sum_{mid=l}^{r-1}dp[l][mid][1]*(dp[mid][r][0]+dp[mid][r][1]) dp[l][r][0]=mid=l∑r−1dp[l][mid][1]∗(dp[mid][r][0]+dp[mid][r][1])
-
d
p
[
l
]
[
r
]
[
1
]
dp[l][r][1]
dp[l][r][1] 的转移:
这里同样是枚举断点 m i d mid mid,显然转移为:
d p [ l ] [ r ] [ 1 ] = ∑ m i d = l r − 1 ( d p [ l ] [ m i d ] [ 0 ] + d p [ l ] [ m i d ] [ 1 ] ) + ( d p [ m i d + 1 ] [ r ] [ 0 ] + d p [ m i d + 1 ] [ r ] [ 1 ] ) dp[l][r][1]=\sum_{mid=l}^{r-1}(dp[l][mid][0]+dp[l][mid][1])+(dp[mid+1][r][0]+dp[mid+1][r][1]) dp[l][r][1]=mid=l∑r−1(dp[l][mid][0]+dp[l][mid][1])+(dp[mid+1][r][0]+dp[mid+1][r][1])
那么只需考虑是否可连,将边界设置为 d p [ i ] [ i ] = 1 dp[i][i]=1 dp[i][i]=1,做区间 d p \tt dp dp 即可,最后答案自然为 d p [ 1 ] [ n ] [ 0 ] + d p [ 1 ] [ n ] [ 1 ] dp[1][n][0]+dp[1][n][1] dp[1][n][0]+dp[1][n][1] 。
Code
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define MAXN 505
#define Int register int
#define Mod 1000000007
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
inline LL Abs(LL x)
{
if (x > 0)
return x;
return - x;
}
inline LL Min(LL x,LL y)
{
return x < y ? x : y;
}
inline LL Max(LL x,LL y)
{
return x > y ? x : y;
}
inline void read(LL &x)
{
x = 0;
LL f = 1;
char s = getchar();
while (s < '0' || s > '9')
{
if (s == '-')
f = -1;
s = getchar();
}
while (s >= '0' && s <= '9')
{
x = (x << 3) + (x << 1) + (s ^ 48);
s = getchar();
}
x *= f;
}
bool Ok[MAXN][MAXN];
LL dp[MAXN][MAXN][2], n;
LL Solve(LL l,LL r,bool Type)
{
if (dp[l][r][Type] != -1)
return dp[l][r][Type];
if (l == r)
return dp[l][r][Type] = 1;
dp[l][r][Type] = 0;
if (! Type)
{
for (Int Mid = l; Mid < r; ++ Mid)
{
if ( Ok[l][Mid] )
{
LL Pre = Solve(l, Mid, 1), Back = Solve(Mid, r, 0);
if ( Ok[Mid][r] )
Back += Solve(Mid, r, 1);
dp[l][r][Type] += Pre * Back, dp[l][r][Type] %= Mod;
}
}
}
else
{
for (Int Mid = l; Mid < r; ++ Mid)
{
LL Pre = Solve(l, Mid, 0);
if ( Ok[l][Mid] )
Pre += Solve(l, Mid, 1);
LL Back = Solve(Mid + 1, r, 0);
if ( Ok[Mid + 1][r] )
Back += Solve(Mid + 1, r, 1);
dp[l][r][Type] += Pre * Back, dp[l][r][Type] %= Mod;
}
}
return dp[l][r][Type];
}
int main()
{
read( n );
for (Int i = 1; i <= n; ++ i)
for (Int j = 1; j <= n; ++ j)
scanf("%d", &Ok[i][j]);
memset(dp, -1, sizeof dp);
LL Ans = Solve(1, n, 0);
if ( Ok[1][n] )
Ans += Solve(1, n, 1);
printf("%lld", Ans % Mod);
return 0;
}