题面
link
有 S 列,B 个正方形,正方形可以叠加摆放在一列上,要求每个正方形左右不能都有正方形比它高(每一列至少一个正方形),问有几种摆法?(
1
≤
B
≤
S
≤
5000
1 ≤B ≤ S ≤ 5000
1≤B≤S≤5000)
分析
从每一列考虑不是很好做,可以从底开始,每一行开始考虑。最下面一行一定要放满
S
S
S 块正方形,倒数第二行如果要放
k
1
k_1
k1 个正方形的话,则有
(
S
−
k
1
+
1
)
(S - k_1 + 1)
(S−k1+1) 种方法(根据要求正方形必须相邻),那么在这个基础上如果倒数第三行放
k
2
k_2
k2 个正方形,有
(
k
1
−
k
2
+
1
)
(k_1 - k_2 + 1)
(k1−k2+1) 种方法。
可以定义
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 表示下面一层的正方形宽度为
i
i
i, 还剩
j
j
j 个正方形总共的方案数,不难得出这样的递推关系:
d
p
[
i
]
[
j
]
=
∑
p
=
1
m
i
n
(
i
,
j
)
(
i
−
p
+
1
)
d
p
[
p
]
[
j
−
p
]
dp[i][j] = \sum_{p = 1}^{min(i, j)} (i - p + 1) \ dp[p][j-p]
dp[i][j]=p=1∑min(i,j)(i−p+1) dp[p][j−p]
其中边界条件为(宽度为1和没有正方形的特殊情况):
d
p
[
1
]
[
j
]
=
1
,
d
p
[
i
]
[
0
]
=
1
dp[1][j] = 1, \ dp[i][0] = 1
dp[1][j]=1, dp[i][0]=1
直接用这个递推式的复杂度为
O
(
n
3
)
O(n^3)
O(n3),需要进行化简。
当
i
>
j
i > j
i>j 时:
d p [ i ] [ j ] = ∑ p = 1 j ( i − p + 1 ) d p [ p ] [ j − p ] = ∑ p = 1 j ( i − 1 − p + 1 ) d p [ p ] [ j − p ] + ∑ p = 1 j d p [ p ] [ j − p ] dp[i][j] = \sum_{p = 1}^{j}(i - p + 1) \ dp[p][j-p] = \sum_{p = 1}^{j}(i - 1 - p + 1) \ dp[p][j-p]+ \sum_{p = 1}^{j} \ dp[p][j-p] dp[i][j]=∑p=1j(i−p+1) dp[p][j−p]=∑p=1j(i−1−p+1) dp[p][j−p]+∑p=1j dp[p][j−p]
= d p [ i − 1 ] [ j ] + ∑ p = 1 m i n ( i , j ) d p [ p ] [ j − p ] = dp[i-1][j]+ \sum_{p = 1}^{min(i, j)} \ dp[p][j-p] =dp[i−1][j]+∑p=1min(i,j) dp[p][j−p]
当 i ≤ j i ≤ j i≤j 时:
d p [ i ] [ j ] = ∑ p = 1 i ( i − p + 1 ) d p [ p ] [ j − p ] = ∑ p = 1 i − 1 ( i − 1 − p + 1 ) d p [ p ] [ j − p ] + ∑ p = 1 i − 1 d p [ p ] [ j − p ] + dp[i][j] = \sum_{p = 1}^{i}(i - p + 1) \ dp[p][j-p] = \sum_{p = 1}^{i-1}(i - 1 - p + 1) \ dp[p][j-p]+ \sum_{p = 1}^{i-1} \ dp[p][j-p] + dp[i][j]=∑p=1i(i−p+1) dp[p][j−p]=∑p=1i−1(i−1−p+1) dp[p][j−p]+∑p=1i−1 dp[p][j−p]+
d p [ i ] [ j − i ] = d p [ i − 1 ] [ j ] + ∑ p = 1 m i n ( i , j ) d p [ p ] [ j − p ] dp[i][j-i] = dp[i-1][j]+ \sum_{p = 1}^{min(i, j)} \ dp[p][j-p] dp[i][j−i]=dp[i−1][j]+∑p=1min(i,j) dp[p][j−p]
所以如果令 s u m [ j ] = ∑ p = 1 m i n ( i , j ) d p [ p ] [ j − p ] sum[j] = \sum_{p = 1}^{min(i, j)} \ dp[p][j-p] sum[j]=∑p=1min(i,j) dp[p][j−p], 记录前缀和,则 d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + s u m [ j ] dp[i][j] = dp[i-1][j]+ sum[j] dp[i][j]=dp[i−1][j]+sum[j]
所以可以大循环是第一维,小循环是第二维的进行遍历,当我们计算完 d p [ i ] [ j ] dp[i][j] dp[i][j] 的时候,可以去更新 s u m [ i + j ] sum[i+j] sum[i+j],而同时在我们计算 d p [ i ] [ j ] dp[i][j] dp[i][j] 的时候, s u m [ j ] sum[j] sum[j] 必然已经通过前面的计算更新完毕。
最后答案就是 d p [ S ] [ B − S ] dp[S][B-S] dp[S][B−S]。
#include <bits/stdc++.h>
#define here printf("modassing [%s] in LINE %d\n", __FUNCTION__, __LINE__);
#define debug(x) cout << #x << ":\t" << (x) << endl;
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> P;
const int maxn = 5010;
const int maxm = 1e6 + 10;
const int INF = 0x3f3f3f3f;
const ll mod = 1e9 + 7;
const double pi = acos(-1.0);
const double eps = 0.0000001;
int B, S;
ll dp[maxn][maxn], sum1[maxn<<1];
int main()
{
scanf("%d %d", &S, &B);
B -= S;
for(int j = 0; j <= B; j++) dp[1][j] = sum1[1+j] = 1;
for (int i = 2; i <= S; i++)
{
dp[i][0] = 1;
sum1[i]++;
for (int j = 1; j <= B; j++)
{
dp[i][j] = (dp[i-1][j] + sum1[j]) % mod;
sum1[i + j] = (sum1[i + j] + dp[i][j]) % mod;
}
}
printf("%lld\n", dp[S][B]);
}