ツバサの備忘録

主に備忘録代わりに精進記録を載せていくつもりです。

2019-2020 ACM-ICPC Latin American Regional Programming Contest F. Fabricating Sculptures

リンク 問題文

解法

1段ずつ下からブロックを積んでいくことを考えます。
ある段のある区間にブロックを積んだら、それ以降はその積んだ区間に対してのみ積んでいくかどうかを考えます。
dp[i][j] = 区間の幅が残りiで、ブロックの残り個数がj個の際の、そこからの構築通り数
とします。
\displaystyle dp[i][j] = \sum_{k = 1}^{i} ((i - k + 1) \times dp[k][j-k])
となります。
少し分解して、
\displaystyle dp[i][j] =(i + 1) \times \sum_{k = 1}^{i}  dp[k][j-k] - \sum_{k = 1}^{i} ( k  \times dp[k][j-k])
とすると、
\displaystyle sum1[i][j] = \sum_{k = 1}^{i}  dp[k][j-k]
\displaystyle sum2[i][j] = \sum_{k = 1}^{i}  (k \times dp[k][j-k])
とすることで、累積和をループの中で取りながらDPをすることができます。
\displaystyle dp[i][j] =(i + 1) \times sum1[i][j] - sum2[i][j]
という式で計算します。

#include <bits/stdc++.h>
#define MOD (long long)(1e9 + 7)
using namespace std;
 
template <int mod>
struct ModInt {
  int x;
  ModInt() : x(0) {}
  ModInt(int64_t y)
      : x(y >= 0 ? y % mod : (mod - (-y) % mod) % mod) {}
  ModInt &operator+=(const ModInt &p) {
    if((x += p.x) >= mod) x -= mod;
    return *this;
  }
  ModInt &operator-=(const ModInt &p) {
    if((x += mod - p.x) >= mod) x -= mod;
    return *this;
  }
  ModInt &operator*=(const ModInt &p) {
    x = (int)(1LL * x * p.x % mod);
    return *this;
  }
  ModInt &operator/=(const ModInt &p) {
    *this *= p.inverse();
    return *this;
  }
  ModInt operator-() const { return ModInt(-x); }
  ModInt operator+(const ModInt &p) const {
    return ModInt(*this) += p;
  }
  ModInt operator-(const ModInt &p) const {
    return ModInt(*this) -= p;
  }
  ModInt operator*(const ModInt &p) const {
    return ModInt(*this) *= p;
  }
  ModInt operator/(const ModInt &p) const {
    return ModInt(*this) /= p;
  }
  bool operator==(const ModInt &p) const {
    return x == p.x;
  }
  bool operator!=(const ModInt &p) const {
    return x != p.x;
  }
  ModInt inverse() const {
    int a = x, b = mod, u = 1, v = 0, t;
    while(b > 0) {
      t = a / b;
      swap(a -= t * b, b);
      swap(u -= t * v, v);
    }
    return ModInt(u);
  }
  ModInt pow(int64_t n) const {
    ModInt ans(1), mul(x);
    while(n) {
      if(n & 1) ans *= mul;
      mul *= mul;
      n >>= 1;
    }
    return ans;
  }
  friend ostream &operator<<(ostream &os, const ModInt &p) {
    return os << p.x;
  }
  friend istream &operator>>(istream &is, ModInt &a) {
    int64_t t;
    is >> t;
    a = ModInt<mod>(t);
    return (is);
  }
  static int get_mod() { return mod; }
};
 
long long s, b;
vector<vector<ModInt<MOD>>> dp, sum1, sum2;
 
ModInt<MOD> solve();
 
int main() {
  cin >> s >> b;
  b -= s;
  cout << solve() << endl;
  return 0;
}
 
ModInt<MOD> solve() {
  dp.assign(s + 1, vector<ModInt<MOD>>(b + 1, 0));
  sum1.assign(s + 1, vector<ModInt<MOD>>(b + 1, 0));
  sum2.assign(s + 1, vector<ModInt<MOD>>(b + 1, 0));
  dp[0][0] = sum1[0][0] = 1;
  for(int i = 1; i <= s; ++i) {
    for(int j = 0; j <= b; ++j) {
      sum1[i][j] += sum1[i - 1][j];
      sum2[i][j] += sum2[i - 1][j];
      if(j != 0) {
        dp[i][j] = sum1[i][j] * (i + 1);
        dp[i][j] -= sum2[i][j];
      }
      else
        dp[i][j] = 1;
      if(i + j <= b) {
        sum1[i][i + j] += dp[i][j];
        sum2[i][i + j] += dp[i][j] * i;
      }
    }
  }
  return dp[s][b];
}