1. 程式人生 > 其它 >[噼昂!]鋪設道路

[噼昂!]鋪設道路

壹、題目描述 ¶

  眾所周知,伊蕾娜是一位美少女魔法師。

  作為一名魔法師,她的主要工作是填平下陷的地表。整段道路可以看作是 \(N\) 塊首尾相連的區域,一開始,第 \(i\) 塊區域下陷的深度為 \(d_i\).

  她每天可以選擇一段連續區間 \([L,R]\) 施法,填充這段區間中的每塊區域,讓其下陷深度減少 \(1\). 在選擇區間時,需要保證,區間內的每塊區域在填充前下陷深度均不為 \(0\).

  伊蕾娜希望你能幫他設計一種方案,可以在最短的時間內將整段道路的下陷深度都變為 \(0\).

  但是每次操作消耗的體力值不一定相同,對於一次施法 \([L, R]\),伊蕾娜會消耗 \((R-L+1)^2\)

的魔力值。

  現在伊蕾娜想要知道在保證時間最短的前提下,她能消耗的體力值的最大值和最小值。

貳、題解 ¶

  不得不說,妙到家了。關鍵是想到對 \(d_i\) 進行差分,定義差分陣列 \(a_i=d_i-d_{i-1}(i\in [1,n+1])\).

  那麼,我們的每一次操作就是將兩個位置 \(i,j(i<j)\) 組合,將 \(a_i\gets a_i-1,a_j\gets a_j+1\),並且花費 \((j-i)^2\).

  因此,我們可以獲得的是,最小時間花費為 \(\sum \max(a_i,0)\). 對於最大最小花費,我們可以用以下方法計算:

  依次遍歷每一個 \(a_i\)

,若 \(a_i>0\),則將 \(a_i\) 加入佇列中;否則,若當前計算最大值,則找隊頭的元素進行匹配,否則找隊尾元素進行匹配,該貪心由均值不等式給出。然後就做完了......時間複雜度 \(\mathcal O(n)\).

叄、參考程式碼 ¶

#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 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 qkgetc() {
# define BUFFERSIZE 1 << 20
        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 CHARRECEI qkgetc()
#else
# define CHARRECEI getchar()
#endif
    template<class T> inline T readret(T x) {
        x = 0; int f = 0; char c;
        while((c = CHARRECEI) < '0' || '9' < c) if(c == '-') f = 1;
        for(x = (c ^ 48); '0' <= (c = CHARRECEI) && c <= '9'; x = (x << 1) + (x << 3) + (c ^ 48));
        return f ? -x : x;
    }
    template<class T> inline void readin(T& x) {
        x = 0; int f = 0; char c;
        while((c = CHARRECEI) < '0' || '9' < c) if(c == '-') f = 1;
        for(x = (c ^ 48); '0' <= (c = CHARRECEI) && c <= '9'; x = (x << 1) + (x << 3) + (c ^ 48));
        if(f) x = -x;
	}
    template<class T, class... Args> inline void readin(T& x, Args&... args) {
        readin(x), readin(args...);
    }
    // default enter
    template<class T> inline void writln(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;

const int maxn = 3e5;
const int mod = 1e9 + 7;

int n, d[maxn + 5], a[maxn + 5];
ll t;

inline void input() {
    readin(n);
    rep(i, 1, n) {
        readin(d[i]);
        a[i] = d[i] - d[i - 1];
        t += max(a[i], 0);
    }
    a[n + 1] = -d[n];
    ++n;
}

// composition : < residual, index >
pii Q[maxn + 5];
int op, ed;
#define pow2(x) (x) * (x)
inline void solve(int opt) {
    ll ans = 0;
    op = 1, ed = 0;
    rep(i, 1, n) {
        if(a[i] > 0) Q[++ed] = {a[i], i};
        else if(a[i] < 0) {
            int cur = -a[i], mn;
            while(cur && op <= ed) {
                if(!opt) {
                    mn = min(Q[op].fi, cur);
                    Q[op].fi -= mn, cur -= mn;
                    ans = (ans + 1ll * pow2(i - Q[op].se) * mn % mod) % mod;
                    if(!Q[op].fi) ++op;
                }
                else {
                    mn = min(Q[ed].fi, cur);
                    Q[ed].fi -= mn, cur -= mn;
                    ans = (ans + 1ll * pow2(i - Q[ed].se) * mn % mod) % mod;
                    if(!Q[ed].fi) --ed;
                }
            }
        }
    }
    writln(ans);
}

signed main() {
    // freopen("road.in", "r", stdin);
    // freopen("road.out", "w", stdout);
    input();
    writln(t);
    solve(1);
    solve(0);
    return 0;
}

肆、關鍵 の 地方 ¶

  有區間操作的時候都往差分上多想想?或者說,區間操作經過差分之後,將會變成兩點匹配。