[噼昂!]铺设道路

壹、题目描述 ¶

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

  作为一名魔法师,她的主要工作是填平下陷的地表。整段道路可以看作是 \(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;
}

肆、关键 の 地方 ¶

  有区间操作的时候都往差分上多想想?或者说,区间操作经过差分之后,将会变成两点匹配。

posted @ 2021-10-26 15:59  Arextre  阅读(41)  评论(0编辑  收藏  举报