in-place な DP にもっと慣れていきたい
問題概要
個の数列がある。 個目の数列は 個の要素からなる。 個目の数列の 番目の要素は と表す。
- 個目の数列から 1 個以下の要素を選び、選んだ要素を元の数列の順番通りに並べた数列を とする。
数列 が狭義単調増加となるように選ぶ方法は何通りあるか、1000000007 で割ったあまりを求めよ (数列が等しくても選び方が異なれば異なるものとする)。
制約
考えたこと
とりあえず、登場する数値は高々 種類しかないようなので、座圧する。そのときの登場しうる数値の種類数を と表すことにする。
さて、この問題を見たとき、次のような DP をしたくなる
- dp[ i ][ j ] := 最初の i 個の数列についての処理を確定したとき、数列の最後の値が j であるようにする操作の個数
このとき、DP は次のような式になる。
- 各 j に対して dp[ i + 1 ][ j ] += dp[ i ][ j ] (なにも選ばない)
- i 番目の数列中の値 k に対して、dp[ i + 1 ][ k ] += sum(dp[ i ][ j ], j = 0, 1, ..., k - 1)
このままでは の計算量となって間に合わない。しかしよく見ると、DP の更新を in-place にすれば 1 個目は無視できる。2 個目の更新については、BIT 上で DP すればそれぞれの値 k に対して の計算量で更新できる。
全体の計算量は となる。
コード
#include <bits/stdc++.h> using namespace std; template<class T> inline bool chmax(T& a, T b) { if (a < b) { a = b; return 1; } return 0; } template<class T> inline bool chmin(T& a, T b) { if (a > b) { a = b; return 1; } return 0; } #define COUT(x) cout << #x << " = " << (x) << " (L" << __LINE__ << ")" << endl template<class T1, class T2> ostream& operator << (ostream &s, pair<T1,T2> P) { return s << '<' << P.first << ", " << P.second << '>'; } template<class T> ostream& operator << (ostream &s, vector<T> P) { for (int i = 0; i < P.size(); ++i) { if (i > 0) { s << " "; } s << P[i]; } return s; } template<class T> ostream& operator << (ostream &s, vector<vector<T> > P) { for (int i = 0; i < P.size(); ++i) { s << endl << P[i]; } return s << endl; } template<class T> ostream& operator << (ostream &s, set<T> P) { for(auto it : P) { s << "<" << it << "> "; } return s << endl; } template<class T1, class T2> ostream& operator << (ostream &s, map<T1,T2> P) { for(auto it : P) { s << "<" << it.first << "->" << it.second << "> "; } return s << endl; } // modint template<int MOD> struct Fp { long long val; constexpr Fp(long long v = 0) noexcept : val(v % MOD) { if (val < 0) val += MOD; } constexpr int getmod() const { return MOD; } constexpr Fp operator - () const noexcept { return val ? MOD - val : 0; } constexpr Fp operator + (const Fp& r) const noexcept { return Fp(*this) += r; } constexpr Fp operator - (const Fp& r) const noexcept { return Fp(*this) -= r; } constexpr Fp operator * (const Fp& r) const noexcept { return Fp(*this) *= r; } constexpr Fp operator / (const Fp& r) const noexcept { return Fp(*this) /= r; } constexpr Fp& operator += (const Fp& r) noexcept { val += r.val; if (val >= MOD) val -= MOD; return *this; } constexpr Fp& operator -= (const Fp& r) noexcept { val -= r.val; if (val < 0) val += MOD; return *this; } constexpr Fp& operator *= (const Fp& r) noexcept { val = val * r.val % MOD; return *this; } constexpr Fp& operator /= (const Fp& r) noexcept { long long a = r.val, b = MOD, u = 1, v = 0; while (b) { long long t = a / b; a -= t * b, swap(a, b); u -= t * v, swap(u, v); } val = val * u % MOD; if (val < 0) val += MOD; return *this; } constexpr bool operator == (const Fp& r) const noexcept { return this->val == r.val; } constexpr bool operator != (const Fp& r) const noexcept { return this->val != r.val; } friend constexpr istream& operator >> (istream &is, Fp<MOD>& x) noexcept { is >> x.val; x.val %= MOD; if (x.val < 0) x.val += MOD; return is; } friend constexpr ostream& operator << (ostream &os, const Fp<MOD>& x) noexcept { return os << x.val; } friend constexpr Fp<MOD> modpow(const Fp<MOD> &a, long long n) noexcept { if (n == 0) return 1; auto t = modpow(a, n / 2); t = t * t; if (n & 1) t = t * a; return t; } }; template <class Abel> struct BIT { Abel UNITY_SUM = 0; vector<Abel> dat; // [0, n) BIT(int n, Abel unity = 0) : UNITY_SUM(unity), dat(n, unity) { } void init(int n) { dat.assign(n, UNITY_SUM); } // a is 0-indexed inline void add(int a, Abel x) { for (int i = a; i < (int)dat.size(); i |= i + 1) dat[i] = dat[i] + x; } // [0, a), a is 0-indexed inline Abel sum(int a) { Abel res = UNITY_SUM; for (int i = a - 1; i >= 0; i = (i & (i + 1)) - 1) res = res + dat[i]; return res; } // [a, b), a and b are 0-indexed inline Abel sum(int a, int b) { return sum(b) - sum(a); } // debug void print() { for (int i = 0; i < (int)dat.size(); ++i) cout << sum(i, i + 1) << ","; cout << endl; } }; const int MOD = 1000000007; using mint = Fp<MOD>; int N; vector<vector<int>> A; vector<int> all; mint solve() { sort(all.begin(), all.end()); all.erase(unique(all.begin(), all.end()), all.end()); int S = all.size(); BIT<mint> bit(S + 10); bit.add(0, 1); for (int i = 0; i < N; ++i) { vector<pair<int,mint>> chs; for (int j = 0; j < A[i].size(); ++j) { int k = lower_bound(all.begin(), all.end(), A[i][j]) - all.begin() + 1; mint add = bit.sum(0, k); chs.emplace_back(k, add); } for (auto p : chs) bit.add(p.first, p.second); } return bit.sum(0, S+2); } int main() { while (cin >> N) { A.assign(N, vector<int>()); all.clear(); for (int i = 0; i < N; ++i) { int M; cin >> M; A[i].resize(M); for (int j = 0; j < M; ++j) cin >> A[i][j], all.push_back(A[i][j]); } cout << solve() << endl; } }