[噼昂!]铺设道路
壹、题目描述 ¶
众所周知,伊蕾娜是一位美少女魔法师。
作为一名魔法师,她的主要工作是填平下陷的地表。整段道路可以看作是 \(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;
}
肆、关键 の 地方 ¶
有区间操作的时候都往差分上多想想?或者说,区间操作经过差分之后,将会变成两点匹配。