[噼昂!]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)\) 表示后缀,定义类似。至于 \(j\) 这一维可能有些奇怪,但是到了后面就可以解释为什么应该这样做了。

由于对称性,只解释 \(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\) 并且 \(sufMax(i+1,s){\color{red}<}h_i\),保证两者都小于等于原因是我们的状态定义就要保证第二维后面还有一座高于它的山,至于为什么 \(sufMax\) 不应该取等,是为了防止计算重复。

时间复杂度 \(\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\) 包含,同时应该注意那些较小的变量,他们可能是问题的关键。而那些既影响问题,同时又被特殊设计的变量,那就更应该被考虑。

posted @ 2021-10-08 15:58  Arextre  阅读(34)  评论(0编辑  收藏  举报