[dp+前缀和优化] codeforces/gym/102428/problem/F Fabricating Sculptures

题面

link
  有 S 列,B 个正方形,正方形可以叠加摆放在一列上,要求每个正方形左右不能都有正方形比它高(每一列至少一个正方形),问有几种摆法?( 1 ≤ B ≤ S ≤ 5000 1 ≤B ≤ S ≤ 5000 1BS5000)

分析

  从每一列考虑不是很好做,可以从底开始,每一行开始考虑。最下面一行一定要放满 S S S 块正方形,倒数第二行如果要放 k 1 k_1 k1 个正方形的话,则有 ( S − k 1 + 1 ) (S - k_1 + 1) (Sk1+1) 种方法(根据要求正方形必须相邻),那么在这个基础上如果倒数第三行放 k 2 k_2 k2 个正方形,有 ( k 1 − k 2 + 1 ) (k_1 - k_2 + 1) (k1k2+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=1min(i,j)(ip+1) dp[p][jp]
  其中边界条件为(宽度为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 ij 时:

   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(ip+1) dp[p][jp]=p=1j(i1p+1) dp[p][jp]+p=1j dp[p][jp]

   = 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[i1][j]+p=1min(i,j) dp[p][jp]

  当 i ≤ j i ≤ j ij 时:

   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(ip+1) dp[p][jp]=p=1i1(i1p+1) dp[p][jp]+p=1i1 dp[p][jp]+

   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][ji]=dp[i1][j]+p=1min(i,j) dp[p][jp]

  

  所以如果令 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][jp], 记录前缀和,则 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[i1][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][BS]

#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]);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值