[噼昂!]water(Pending)
壹、關於題目 ¶
不想編了。
貳、關於題解 ¶
必須注意到,最終剩下的最高的山一定是前 \(K+1\) 高的。
該特性重要的原因是,接水的高度一定會取決於最高的山,因此我們應該注意到這一關於最高的山的特性。
然後,我們可以設計 \(\rm DP\) 轉移了,設 \(pre(i,j,k,0|1)\) 表示前 \(i\) 座山,目前剩下的最高高度的山是前 \(i\) 座中第 \(j\) 高的,並且保證 \([i+1,n]\) 中也有高度至少為 \(j\) 的山,目前已經移除了 \(k\) 座山,積水體積為 偶數/奇數 的方案數。同時,定義 \(suf(i,j,k,0|1)\)
由於對稱性,只解釋 \(pre\) 的轉移。考慮從 \(i-1,j,k,0|1\) 轉移到 \(i,j',k',w'\).
記 \(preMax(i,j)\) 表示前 \(i\) 座山中前 \(K+1\) 高的山中第 \(j\) 矮的高度,如果不足,用 \(0\) 表示
先找到 \(j'\) 使得 \(preMax(i-1,j)=preMax(i,j')\),如果找不到,則 \(j'=0\)(表示最小的),然後,我們分開考慮是否移除當前的山;
- 如果移除該山,顯然前提條件是 \(k<K\),那麼該位的積水就應該是上一次最高的山的高度,為 \(preMax(i-1,j)\)
- 如果該山保留,那麼轉移不一定到 \(j'\),因為 \(i\) 這座山也有可能成為最高山,所以需要比較一下 \(preMax(i-1,j)\) 與 \(h_i\) 的大小,如果 \(h_i\) 更大,那麼應該轉移到使得 \(preMax(i,t)=h_i\) 的 \(t\) 位,否則,仍然轉移到 \(j'\);
至於 \(suf()\) 也是類似的轉移。另外,我們需要考慮合併 \(pre()\) 與 \(suf()\),具體即列舉一座前 \(K+1\) 高的山,然後將 \(pre(i-1,p)\) 與 \(suf(i+1,s)\) 進行合併,注意,此處需要保證 \(preMax(i-1,p)\le h_i\)
時間複雜度 \(\mathcal O(nk^2)\),不過我寫得不是很好,常數過大。
叄、參考程式碼 ¶
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
using namespace std;
// # define USING_STDIN
// # define NDEBUG
// # define NCHECK
#include <cassert>
namespace Elaina {
#define rep(i, l, r) for(int i = (l), i##_end_ = (r); i <= i##_end_; ++i)
#define drep(i, l, r) for(int i = (l), i##_end_ = (r); i >= i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define whole(v) ((v).begin()), ((v).end())
#define bitcnt(s) (__builtin_popcount(s))
#ifdef NCHECK
# define iputs(Content) ((void)0)
# define iprintf(Content, argvs...) ((void)0)
#else
# define iputs(Content) fprintf(stderr, Content)
# define iprintf(Content, argvs...) fprintf(stderr, Content, argvs)
#endif
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair <int, int> pii;
typedef pair <ll, ll> pll;
template <class T> inline T fab(T x) { return x < 0 ? -x : x; }
template <class T> inline void getmin(T& x, const T rhs) { x = min(x, rhs); }
template <class T> inline void getmax(T& x, const T rhs) { x = max(x, rhs); }
#ifndef USING_STDIN
inline char freaGET() {
# define BUFFERSIZE 1 << 17
static char BUF[BUFFERSIZE], *p1 = BUF, *p2 = BUF;
return p1 == p2 && (p2 = (p1 = BUF) + fread(BUF, 1, BUFFERSIZE, stdin), p1 == p2) ? EOF : *p1++;
# undef BUFFERSIZE
}
# define CHARGET freaGET()
#else
# define CHARGET getchar()
#endif
template <class T> inline T readret(T x) {
x=0; int f = 0; char c;
while((c = CHARGET) < '0' || '9' < c) if(c == '-') f = 1;
for(x = (c^48); '0' <= (c = CHARGET) && c <= '9'; x = (x << 1) + (x << 3) + (c ^ 48));
return f ? -x : x;
}
template <class T> inline void readin(T& x) { x = readret(T(1)); }
template <class T, class... Args> inline void readin(T& x, Args&... args) {
readin(x), readin(args...);
}
template <class T> inline void writc(T x, char s = '\n') {
static int fwri_sta[55], fwri_ed = 0;
if(x < 0) putchar('-'), x = -x;
do fwri_sta[++fwri_ed] = x % 10, x /= 10; while(x);
while(putchar(fwri_sta[fwri_ed--] ^ 48), fwri_ed);
putchar(s);
}
} using namespace Elaina;
/**
* @param MOD used for modulo
* @param RT the primitive root of @p MOD
*/
template <int MOD, int RT> struct Mint {
int val;
static const int mod = MOD;
Mint(ll v = 0) { val = int(-mod < v && v < mod ? v : v % mod); if(val < 0) val += mod; }
inline friend bool operator == (const Mint& a, const Mint& b) { return a.val == b.val; }
inline friend bool operator != (const Mint& a, const Mint& b) { return !(a == b); }
inline friend bool operator < (const Mint& a, const Mint& b) { return a.val < b.val; }
inline friend bool operator > (const Mint& a, const Mint& b) { return a.val > b.val; }
inline friend bool operator <= (const Mint& a, const Mint& b) { return a.val <= b.val; }
inline friend bool operator >= (const Mint& a, const Mint& b) { return a.val >= b.val; }
inline Mint& operator += (const Mint& rhs) { return (*this) = Mint((*this).val + rhs.val); }
inline Mint& operator -= (const Mint& rhs) { return (*this) = Mint((*this).val - rhs.val); }
inline Mint& operator *= (const Mint& rhs) { return (*this) = Mint(1ll * (*this).val * rhs.val); }
inline Mint operator - () const { return Mint(-val); }
inline Mint& operator ++ () { return (*this) = (*this) + 1; }
inline Mint& operator -- () { return (*this) = (*this) - 1; }
inline friend Mint operator + (Mint a, const Mint& b) { return a += b; }
inline friend Mint operator - (Mint a, const Mint& b) { return a -= b; }
inline friend Mint operator * (Mint a, const Mint& b) { return a *= b; }
inline friend Mint qkpow(Mint a, ll n) {
assert(n >= 0); Mint ret = 1;
for(; n; n >>=1, a *= a) if(n & 1) ret *= a;
return ret;
}
inline friend Mint inverse(Mint a) { assert(a != 0); return qkpow(a, mod - 2); }
};
using mint = Mint <1000000007, 5>;
const int maxn = 25000;
const int maxk = 26;
const int mod = 1e9 + 7;
int h[maxn + 5], n, K;
inline void input() {
readin(n, K);
rep(i, 1, n) readin(h[i]);
}
namespace Dynamic_programming {
set <pii> S;
int preMax[maxn + 5][maxk + 5];
int preMax_cnt[maxn + 5], idpre[maxn + 5];
mint pre[maxn + 5][maxk + 5][maxk + 5][2];
inline void solvePre() {
rep(i, 1, n) {
S.insert({h[i], i});
if((int)S.size() > K + 1) S.erase(S.begin());
for(auto x : S) {
preMax[i][++preMax_cnt[i]] = x.fi;
if(x.fi == h[i]) idpre[i] = preMax_cnt[i];
}
}
pre[0][0][0][0] = 1;
rep(i, 1, n) rep(j, 0, preMax_cnt[i - 1]) {
int nxt = 0;
/// find the current id
for(int p = 0; p <= preMax_cnt[i]; ++p)
if(preMax[i - 1][j] == preMax[i][p])
nxt = p;
rep(p, 0, K) {
rep(bit, 0, 1) {
/// try to remove @p i
if(p < K) {
int val = (bit + preMax[i][nxt]) & 1;
pre[i][nxt][p + 1][val] += pre[i - 1][j][p][bit];
}
/// do not remove @p i
if(h[i] > preMax[i - 1][j]) // it becomees the highest Mount
pre[i][idpre[i]][p][bit] += pre[i - 1][j][p][bit];
else {
int val = (bit + preMax[i - 1][j] - h[i]) & 1;
pre[i][nxt][p][val] += pre[i - 1][j][p][bit];
}
}
}
}
}
int sufMax[maxn + 5][maxk + 5];
int sufMax_cnt[maxn + 5], idsuf[maxn + 5];
mint suf[maxn + 5][maxk + 5][maxk + 5][2];
inline void solveSuf() {
S.clear();
drep(i, n, 1) {
S.insert({h[i], -i}); // When same heit, save the latter one
if((int)S.size() > K + 1) S.erase(S.begin());
for(auto x : S) {
sufMax[i][++sufMax_cnt[i]] = x.fi;
if(x.fi == h[i]) idsuf[i] = sufMax_cnt[i];
}
}
suf[n + 1][0][0][0] = 1;
drep(i, n, 1) rep(j, 0, sufMax_cnt[i + 1]) {
int nxt = 0;
for(int p = 0; p <= sufMax_cnt[i]; ++p)
if(sufMax[i + 1][j] == sufMax[i][p])
nxt = p;
rep(p, 0, K) rep(bit, 0, 1) {
// try to remove
if(p < K) {
int val = (bit + sufMax[i][nxt]) & 1;
suf[i][nxt][p + 1][val] += suf[i + 1][j][p][bit];
}
if(h[i] > sufMax[i + 1][j])
suf[i][idsuf[i]][p][bit] += suf[i + 1][j][p][bit];
else {
int val = (bit + sufMax[i + 1][j] - h[i]) & 1;
suf[i][nxt][p][val] += suf[i + 1][j][p][bit];
}
}
}
}
} // using Dynamic_programming;
namespace Combine {
using namespace Dynamic_programming;
mint ans;
inline void launch() {
S.clear();
rep(i, 1, n) {
S.insert({h[i], i});
if((int)S.size() > K + 1)
S.erase(S.begin());
}
for(auto x : S) {
int i = x.se;
for(int p = 0; p <= preMax_cnt[i - 1]; ++p) if(preMax[i - 1][p] <= x.fi)
for(int s = 0; s <= sufMax_cnt[i + 1]; ++s) if(sufMax[i + 1][s] < x.fi /* avoid calc the same situation */ ) {
rep(t, 0, K) {
ans += pre[i - 1][p][t][0] * suf[i + 1][s][K - t][0];
ans += pre[i - 1][p][t][1] * suf[i + 1][s][K - t][1];
}
}
}
writc(ans.val);
}
} // using namespace Combine;
signed main() {
// freopen("rain.in", "r", stdin);
// freopen("rain.out", "w", stdout);
input();
Dynamic_programming::solvePre();
Dynamic_programming::solveSuf();
Combine::launch();
return 0;
}
肆、關鍵 の 地方 ¶
影響問題的因素,往往應該被 \(\rm DP\) 包含,同時應該注意那些較小的變數,他們可能是問題的關鍵。而那些既影響問題,同時又被特殊設計的變數,那就更應該被考慮。