[噼昂!]鋪設道路
阿新 • • 發佈:2021-10-26
壹、題目描述 ¶
眾所周知,伊蕾娜是一位美少女魔法師。
作為一名魔法師,她的主要工作是填平下陷的地表。整段道路可以看作是 \(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\)
叄、參考程式碼 ¶
#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; }
肆、關鍵 の 地方 ¶
有區間操作的時候都往差分上多想想?或者說,區間操作經過差分之後,將會變成兩點匹配。