Description
有
N
N
N 座山横着排成一行,从左到右编号为从
0
0
0 到
N
−
1
N−1
N−1。山
i
i
i 的高度为
H
i
(
0
≤
i
≤
N
−
1
)
Hi( 0≤i≤N−1 )
Hi(0≤i≤N−1)。每座山的顶上恰好住着一个人。
你打算举行 Q Q Q 个会议,编号为从 0 0 0 到 Q − 1 Q−1 Q−1。会议 j ( 0 ≤ j ≤ Q − 1 ) j( 0≤j≤Q−1 ) j(0≤j≤Q−1)的参加者为住在从山 L j Lj Lj 到山 R j Rj Rj(包括 LjLj 和 RjRj )上的人 ( 0 ≤ L j ≤ R j ≤ N − 1 ) ( 0≤Lj≤Rj≤N−1 ) (0≤Lj≤Rj≤N−1)。对于该会议,你必须选择某个山 x x x 做为会议举办地 ( L j ≤ x ≤ R j ) ( Lj≤x≤Rj ) (Lj≤x≤Rj)。举办该会议的成本与你的选择有关,其计算方式如下:
来自每座山
y
(
L
j
≤
y
≤
R
j
)
y(Lj≤y≤Rj)
y(Lj≤y≤Rj)的参会者的成本,等于在山
x
x
x 和 yy 之间(包含
x
x
x 和
y
y
y )的所有山的最大高度。特别地,来自山
x
x
x 的参会者的成本是
H
x
Hx
Hx,也就是山
x
x
x 的高度。
会议的成本等于其所有参会者的成本之和。
你想要用最低的成本来举办每个会议。
注意,所有的参会者将在每次会议后回到他们自己的山;所以一个会议的成本不会受到先前会议的影响。
Sample Input
4 2
2 4 3 5
0 2
1 3
Sample Output
10
12
L O J LOJ LOJ可以以传统方式提交。
我们考虑离线完成这个问题,我们把一个询问挂在这个区间的最大值上。
设区间
[
l
,
r
]
[l,r]
[l,r]的答案为
f
[
l
]
[
r
]
f[l][r]
f[l][r],最大值的位置为
m
i
d
mid
mid。
那么
f
[
l
]
[
r
]
=
m
i
n
(
f
[
l
]
[
m
i
d
−
1
]
+
(
r
−
m
i
d
+
1
)
∗
h
[
m
i
d
]
,
f
[
m
i
d
+
1
]
[
r
]
+
(
m
i
d
−
l
+
1
)
∗
h
[
m
i
d
]
)
f[l][r]=min(f[l][mid-1]+(r-mid+1)*h[mid],f[mid+1][r]+(mid-l+1)*h[mid])
f[l][r]=min(f[l][mid−1]+(r−mid+1)∗h[mid],f[mid+1][r]+(mid−l+1)∗h[mid])
那么这个结构就是一棵笛卡尔树。
对于每一个右端点,或左端点,我们考虑维护他到当前分治中心的答案。
左端点或右端点的问题你可以把这个串正反跑两遍,相当于你每次只用维护某个点作为右端点到分治中心的答案即可。
考虑更新某一段区间到分治中心的答案。
设我当前区间为
[
l
,
r
]
[l,r]
[l,r],分治中心为
[
l
,
r
]
[l,r]
[l,r]
那么我们对于线段树上的
[
m
i
d
+
1
,
r
]
[mid+1,r]
[mid+1,r]中的每一个
i
i
i都要让他们的值变成:
f
[
l
]
[
i
]
=
m
i
n
(
f
[
l
]
[
m
i
d
−
1
]
+
(
i
−
m
i
d
+
1
)
∗
h
[
m
i
d
]
,
f
[
m
i
d
+
1
]
[
i
]
+
(
m
i
d
−
l
+
1
)
∗
h
[
m
i
d
]
)
f[l][i]=min(f[l][mid-1]+(i-mid+1)*h[mid],f[mid+1][i]+(mid-l+1)*h[mid])
f[l][i]=min(f[l][mid−1]+(i−mid+1)∗h[mid],f[mid+1][i]+(mid−l+1)∗h[mid])。
事实上这个区间中肯定会以一个点作为分界线,满足左边的值都取
f
[
l
]
[
m
i
d
−
1
]
+
(
r
−
m
i
d
+
1
)
∗
h
[
m
i
d
]
f[l][mid-1]+(r-mid+1)*h[mid]
f[l][mid−1]+(r−mid+1)∗h[mid],右边的值都取
f
[
m
i
d
+
1
]
[
i
]
+
(
m
i
d
−
l
+
1
)
∗
h
[
m
i
d
]
f[mid+1][i]+(mid-l+1)*h[mid]
f[mid+1][i]+(mid−l+1)∗h[mid]。
那么可以通过在线段树上二分实现这个修改。
考虑证明上面的结论:
f
[
l
]
[
m
i
d
−
1
]
+
(
i
−
m
i
d
+
1
)
∗
h
[
m
i
d
]
−
f
[
m
i
d
+
1
]
[
i
]
−
(
m
i
d
−
l
+
1
)
∗
h
[
m
i
d
]
<
=
f
[
l
]
[
m
i
d
−
1
]
+
(
(
i
+
1
)
−
m
i
d
+
1
)
∗
h
[
m
i
d
]
−
f
[
m
i
d
+
1
]
[
i
+
1
]
−
(
m
i
d
−
l
+
1
)
∗
h
[
m
i
d
]
f[l][mid-1]+(i-mid+1)*h[mid]-f[mid+1][i]-(mid-l+1)*h[mid] <= f[l][mid-1]+((i+1)-mid+1)*h[mid]-f[mid+1][i+1]-(mid-l+1)*h[mid]
f[l][mid−1]+(i−mid+1)∗h[mid]−f[mid+1][i]−(mid−l+1)∗h[mid]<=f[l][mid−1]+((i+1)−mid+1)∗h[mid]−f[mid+1][i+1]−(mid−l+1)∗h[mid]
=
>
=>
=>
f
[
m
i
d
+
1
]
[
i
+
1
]
−
f
[
m
i
d
+
1
]
[
i
]
<
=
h
[
m
i
d
]
f[mid+1][i+1]-f[mid+1][i] <= h[mid]
f[mid+1][i+1]−f[mid+1][i]<=h[mid]
这个式子显然是满足的,即得证。
L O J LOJ LOJ代码, U O J UOJ UOJ简单魔改就好了。
#include <ctime>
#include <cstdio>
#include <vector>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef long long LL;
LL _min(LL x, LL y) {return x < y ? x : y;}
const int N = 750001;
int read() {
int s = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * f;
}
void put(int x) {
if(x >= 10) put(x / 10);
putchar(x % 10 + '0');
}
LL ans[N];
int n, Q, h[N];
struct st_table {
int f[20][N], bin[21], Log[N];
int _max(int x, int y) {return h[x] >= h[y] ? x : y;}
void bt() {
bin[0] = 1; for(int i = 1; i <= 20; i++) bin[i] = bin[i - 1] * 2;
Log[1] = 0; for(int i = 2; i <= n; i++) Log[i] = Log[i >> 1] + 1;
for(int i = 1; i <= n; i++) f[0][i] = i;
for(int i = 1; bin[i] <= n; i++) {
for(int j = 1; j <= n - bin[i] + 1; j++) {
f[i][j] = _max(f[i - 1][j], f[i - 1][j + bin[i - 1]]);
}
}
}
int query(int l, int r) {
int hh = Log[r - l + 1];
return _max(f[hh][l], f[hh][r - bin[hh] + 1]);
}
} st;
struct query{int l, r;} g[N];
struct cc {LL a, b;};
vector<int> q[N];
struct tnode {
int lc, rc; LL c;
cc ad, cg;
};
struct segment {
tnode t[N << 1];
cc n1, n2;
int cnt;
void bt(int l, int r) {
int now = ++cnt;
t[now].lc = t[now].rc = -1;
t[now].c = 0, t[now].ad = t[now].cg = cc{0, 0};
if(l < r) {
int mid = (l + r) / 2;
t[now].lc = cnt + 1; bt(l, mid);
t[now].rc = cnt + 1; bt(mid + 1, r);
}
}
void build() {cnt = 0; bt(1, n);}
void change(int now, int r, cc x) {
t[now].cg = x; t[now].ad = cc{0, 0};
t[now].c = x.a + x.b * r;
}
void add(int now, int r, cc x) {
if(t[now].cg.a || t[now].cg.b) t[now].cg.a += x.a, t[now].cg.b += x.b;
else t[now].ad.a += x.a, t[now].ad.b += x.b;
t[now].c += x.a + x.b * r;
}
void pushdown(int now, int l, int r) {
int mid = (l + r) / 2;
if(t[now].cg.a || t[now].cg.b) {
change(t[now].lc, mid, t[now].cg);
change(t[now].rc, r, t[now].cg);
t[now].cg = cc{0, 0};
} if(t[now].ad.a || t[now].ad.b) {
add(t[now].lc, mid, t[now].ad);
add(t[now].rc, r, t[now].ad);
t[now].ad = cc{0, 0};
}
}
void Link(int now, int l, int r, int p, LL c) {
if(l == r) {t[now].c = c; return ;}
pushdown(now, l, r);
int mid = (l + r) / 2;
if(p <= mid) Link(t[now].lc, l, mid, p, c);
else Link(t[now].rc, mid + 1, r, p, c);
t[now].c = t[t[now].rc].c;
}
LL query(int now, int l, int r, int p) {
if(l == r) return t[now].c;
pushdown(now, l, r);
int mid = (l + r) / 2;
if(p <= mid) return query(t[now].lc, l, mid, p);
else return query(t[now].rc, mid + 1, r, p);
}
void G1(int now, int l, int r, int ll, int rr) {
if(ll == l && rr == r) {add(now, r, n1); return ;}
pushdown(now, l, r);
int mid = (l + r) / 2;
if(rr <= mid) G1(t[now].lc, l, mid, ll, rr);
else if(ll > mid) G1(t[now].rc, mid + 1, r, ll, rr);
else G1(t[now].lc, l, mid, ll, mid), G1(t[now].rc, mid + 1, r, mid + 1, rr);
t[now].c = t[t[now].rc].c;
}
void G2(int now, int l, int r, int ll, int rr) {
if(ll == l && rr == r) {change(now, r, n2); return ;}
pushdown(now, l, r);
int mid = (l + r) / 2;
if(rr <= mid) G2(t[now].lc, l, mid, ll, rr);
else if(ll > mid) G2(t[now].rc, mid + 1, r, ll, rr);
else G2(t[now].lc, l, mid, ll, mid), G2(t[now].rc, mid + 1, r, mid + 1, rr);
t[now].c = t[t[now].rc].c;
}
void gai(int now, int l, int r, int ll, int rr) {
if(l == r) {
t[now].c = _min(n2.a + n2.b * l, t[now].c + n1.a);
return ;
} pushdown(now, l, r);
int mid = (l + r) / 2;
if(rr <= mid) gai(t[now].lc, l, mid, ll, rr);
else if(ll > mid) gai(t[now].rc, mid + 1, r, ll, rr);
else {
if(t[t[now].lc].c + n1.a <= n2.a + n2.b * mid) {
gai(t[now].lc, l, mid, ll, mid);
G1(t[now].rc, mid + 1, r, mid + 1, rr);
} else {
gai(t[now].rc, mid + 1, r, mid + 1, rr);
G2(t[now].lc, l, mid, ll, mid);
}
} t[now].c = t[t[now].rc].c;
}
void modify(int l, int r, LL a, LL b, LL c) {
n1.a = b, n1.b = 0;
n2.a = a - c * l + c, n2.b = c;
gai(1, 1, n, l, r);
}
} t;
LL dfs(int l, int r) {
if(l > r) return 0;
if(l == r) {t.Link(1, 1, n, l, h[l]); return h[l];}
int mid = st.query(l, r);
LL lans = dfs(l, mid - 1);
LL rans = dfs(mid + 1, r);
t.Link(1, 1, n, mid, lans + h[mid]);
for(int i = 0; i < q[mid].size(); i++) {
int x = q[mid][i];
int ll = g[x].l, rr = g[x].r;
if(mid < rr) ans[x] = _min(ans[x], t.query(1, 1, n, rr) + (LL)(mid - ll + 1) * h[mid]);
} if(mid < r) t.modify(mid + 1, r, lans + h[mid], (LL)h[mid] * (mid - l + 1), h[mid]);
return t.query(1, 1, n, r);
}
void solve() {
st.bt();
for(int i = 1; i <= n; i++) q[i].clear();
for(int i = 1; i <= Q; i++) {
if(g[i].l == g[i].r) ans[i] = h[g[i].l];
else q[st.query(g[i].l, g[i].r)].push_back(i);
} t.build();
dfs(1, n);
}
int main() {
memset(ans, 63, sizeof(ans));
n = read(), Q = read();
for(int i = 1; i <= n; i++) h[i] = read();
for(int i = 1; i <= Q; i++) g[i].l = read() + 1, g[i].r = read() + 1;
solve();
reverse(h + 1, h + n + 1);
for(int i = 1; i <= Q; i++) g[i].l = n - g[i].l + 1, g[i].r = n - g[i].r + 1, swap(g[i].l, g[i].r);
solve();
for(int i = 1; i <= Q; i++) printf("%lld\n", ans[i]);
return 0;
}