【学习笔记】CDQ
一、算法介绍
C D Q CDQ CDQ 分治,类似于归并排序,大体思路为:将需要求解的询问区间 [ l , r ] [l, r] [l,r] 分为 [ l , m i d ] , [ m i d + 1 , r ] [l, mid], [mid + 1, r] [l,mid],[mid+1,r] 然后分别求解只在这两个区间中的,再求解 i ∈ [ l , m i d ] , j ∈ [ m i d + 1 , r ] i \in [l, mid],j\in[mid + 1, r] i∈[l,mid],j∈[mid+1,r] 的贡献。
二、例题
1.三位偏序(陌上花开)
模板,见注释。
#include <map>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define fi first
#define se second
#define db double
#define LL long long
#define ULL unsigned long long
#define PII pair <int, int>
#define MP(x,y) make_pair (x, y)
#define rep(i,j,k) for (int i = (j); i <= (k); ++i)
#define per(i,j,k) for (int i = (j); i >= (k); --i)
template <typename T> T Max (T x, T y) { return x > y ? x : y; }
template <typename T> T Min (T x, T y) { return x < y ? x : y; }
template <typename T> T Abs (T x) { return x > 0 ? x : -x; }
template <typename T>
void read (T &x) {
x = 0; T f = 1;
char ch = getchar ();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar ();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + ch - '0';
ch = getchar ();
}
x *= f;
}
template <typename T, typename... Args>
void read (T &x, Args&... args) {
read (x); read (args...);
}
char For_Print[25];
template <typename T>
void write (T x) {
if (x == 0) { putchar ('0'); return; }
if (x < 0) { putchar ('-'); x = -x; }
int poi = 0;
while (x) {
For_Print[++poi] = x % 10 + '0';
x /= 10;
}
while (poi) putchar (For_Print[poi--]);
}
template <typename T>
void print (T x, char ch) {
write (x); putchar (ch);
}
const LL Mod = 1e9 + 7;
LL square (LL x) { return (x * x) % Mod; }
void DEL (LL &x, LL y) { ((x -= y) < 0) && (x += Mod); }
void ADD (LL &x, LL y) { ((x += y) >= Mod) && (x -= Mod); }
const int Maxn = 2 * 1e5;
int n;
LL bak_ans[Maxn + 5];
//答案桶
LL BIT[Maxn + 5];
int lowbit (int x) { return x & -x; }
void Update (int Index, LL x) {
for (int i = Index; i <= Maxn; i += lowbit (i))
BIT[i] += x;
}
LL Sum (int Index) {
LL res = 0;
for (int i = Index; i >= 1; i -= lowbit (i))
res += BIT[i];
return res;
}
//树状数组板子
map <pair <int, PII>, int> bak;
int cnt;
struct Node {
int a, b, c, num; //由于不求每个询问的答案,只求个数,我们就不需要保存它来自第几个询问。
LL ans;//记录答案
}a[Maxn + 5], q[Maxn + 5], tmp[Maxn + 5];
bool cmp (Node x, Node y) { //排序
if (x.a != y.a) return x.a < y.a;
else if (x.b != y.b) return x.b < y.b;
else return x.c < y.c;
}
void CDQ (int l, int r) {
if (l == r) return;
int mid = (l + r) >> 1;
CDQ (l, mid); CDQ (mid + 1, r);
// 分裂区间
int i = l, j = mid + 1, k = l;
while (i <= mid && j <= r) {
if (q[i].b <= q[j].b) {
// q[i] 可能对 q[j] 作贡献
Update (q[i].c, q[i].num);
// 在树状数组上加入 q[i] 的有贡献的取值区间。
tmp[k++] = q[i];
i++;
}
else {
q[j].ans += Sum (q[j].c);
// 统计 q[j] 的答案
tmp[k++] = q[j];
j++;
}
}
while (i <= mid) { Update (q[i].c, q[i].num); tmp[k++] = q[i]; i++; }
while (j <= r) { q[j].ans += Sum (q[j].c); tmp[k++] = q[j]; j++; }
//归并板子
while (i - 1 >= l) { i--; Update (q[i].c, -q[i].num); } // 清空树状数组
for (k = l; k <= r; k++) q[k] = tmp[k]; // 归并板子
}
int main () {
// freopen ("D:\\lihan\\1.in", "r", stdin);
// freopen ("D:\\lihan\\1.out", "w", stdout);
int tem; read (n); read (tem);
rep (i, 1, n) {
read (a[i].a, a[i].b, a[i].c);
pair <int, PII> tmp = MP (a[i].a, MP (a[i].b, a[i].c));
if (bak.find (tmp) == bak.end ()) //相同的只算一个
bak[tmp] = 0;
bak[tmp]++;
}
rep (i, 1, n) {
pair <int, PII> tmp = MP (a[i].a, MP (a[i].b, a[i].c));
if (bak.find (tmp) != bak.end ()) {
q[++cnt] = a[i]; q[cnt].num = bak[tmp];
bak.erase (tmp);
}
}
sort (q + 1, q + 1 + cnt, cmp);
CDQ (1, cnt);
rep (i, 1, cnt) {
bak_ans[q[i].ans + q[i].num - 1] += q[i].num;
//等级应为 q[i].ans + q[i].num - 1,因为我只统计了 q[j].a/b/c <= q[i].a/b/c,并且 q[j] != q[i] 的个数。
}
rep (i, 0, n - 1) {
print (bak_ans[i], '\n');
}
return 0;
}
2.动态逆序对
我们需要统计多少在前面删除操作满足 q [ j ] . i d x < q [ i ] . i d x , q [ j ] . v a l > q [ i ] . v a l q[j].idx < q[i].idx,q[j].val > q[i].val q[j].idx<q[i].idx,q[j].val>q[i].val,或者 q [ j ] . i d x > q [ i ] . i d x , q [ j ] . v a l < q [ i ] . v a l q[j].idx > q[i].idx, q[j].val < q[i].val q[j].idx>q[i].idx,q[j].val<q[i].val,这样,我们删掉 i i i 后会少的逆序对数量就是:在原序列删除后的贡献 - 统计的满足要求的操作个数(即减少的逆序对数量所受的影响),然后求个前缀和就行了。
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define fi first
#define se second
#define db double
#define LL long long
#define ULL unsigned long long
#define PII pair <int, int>
#define MP(x,y) make_pair (x, y)
#define rep(i,j,k) for (int i = (j); i <= (k); ++i)
#define per(i,j,k) for (int i = (j); i >= (k); --i)
template <typename T> T Max (T x, T y) { return x > y ? x : y; }
template <typename T> T Min (T x, T y) { return x < y ? x : y; }
template <typename T> T Abs (T x) { return x > 0 ? x : -x; }
template <typename T>
void read (T &x) {
x = 0; T f = 1;
char ch = getchar ();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar ();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + ch - '0';
ch = getchar ();
}
x *= f;
}
template <typename T, typename... Args>
void read (T &x, Args&... args) {
read (x); read (args...);
}
char For_Print[25];
template <typename T>
void write (T x) {
if (x == 0) { putchar ('0'); return; }
if (x < 0) { putchar ('-'); x = -x; }
int poi = 0;
while (x) {
For_Print[++poi] = x % 10 + '0';
x /= 10;
}
while (poi) putchar (For_Print[poi--]);
}
template <typename T>
void print (T x, char ch) {
write (x); putchar (ch);
}
const LL Mod = 1e9 + 7;
LL square (LL x) { return (x * x) % Mod; }
void DEL (LL &x, LL y) { ((x -= y) < 0) && (x += Mod); }
void ADD (LL &x, LL y) { ((x += y) >= Mod) && (x -= Mod); }
const int Maxn = 1e5;
int n, m;
int b[Maxn + 5], cost[Maxn + 5];
int a[Maxn + 5], id[Maxn + 5];
LL ans[Maxn + 5];
LL BIT[Maxn * 3 + 5];
int lowbit (int x) { return x & -x; }
void Update (int Index, LL x) {
Index += n + 1; // 防止负数暴毙
for (int i = Index; i <= Maxn * 3; i += lowbit (i))
BIT[i] += x;
}
LL Sum (int Index) {
Index += n + 1; // 防止负数暴毙
LL res = 0;
for (int i = Index; i >= 1; i -= lowbit (i))
res += BIT[i];
return res;
}
int cnt = 0;
struct qst {
int idx, val, id, ans;
} q[Maxn + 5], tmp[Maxn + 5];
bool cmp (qst x, qst y) {
return x.idx < y.idx;
}
void CDQ (int l, int r) {
if (l == r) return;
int mid = (l + r) >> 1;
CDQ (l, mid); CDQ (mid + 1, r);
int i = l, j = mid + 1, k = l;
while (i <= mid && j <= r) {
if (q[i].idx < q[j].idx) {
Update (q[i].val, 1);
tmp[k++] = q[i++];
}
else {
q[j].ans += Sum (q[j].val);
tmp[k++] = q[j++];
}
}
while (i <= mid) { Update (q[i].val, 1); tmp[k++] = q[i++]; }
while (j <= r) { q[j].ans += Sum (q[j].val); tmp[k++] = q[j++]; }
while (i - 1 >= l) { Update (q[--i].val, -1); }
rep (k, l, r) q[k] = tmp[k];
} // CDQ 板子
int main () {
// freopen ("D:\\lihan\\1.in", "r", stdin);
// freopen ("D:\\lihan\\1.out", "w", stdout);
LL res = 0;
read (n, m);
rep (i, 1, n) {
read (a[i]);
id[a[i]] = i;
Update (a[i], 1);
cost[i] += i - Sum (a[i]); // 记录在原序列中的逆序对个数
res += i - Sum (a[i]); // 统计逆序对个数
}
rep (i, 1, n) Update (i, -1);
per (i, n, 1) {
cost[i] += Sum (a[i]); // 记录在原序列中的逆序对个数
Update (a[i], 1);
}
rep (i, 1, n) Update (i, -1);
cnt = 0;
rep (i, 1, m) {
read (b[i]); int x; x = b[i];
q[++cnt] = { n - id[x], x, i, 0 };
//取负相当于使不等号变向,下标大的变为下标小的
}
CDQ (1, cnt);
rep (i, 1, cnt)
ans[q[i].id] += q[i].ans;
cnt = 0;
rep (i, 1, m) {
int x; x = b[i];
q[++cnt] = { id[x], -x, i, 0 };
//取负相当于使不等号变向,值大的变为值小的。
}
CDQ (1, cnt);
rep (i, 1, cnt) {
ans[q[i].id] += q[i].ans;
ans[q[i].id] = cost[id[b[q[i].id]]] - ans[q[i].id];
// id[b[q[i].id]] 映射会原来的序列位置
// 计算本次删除会少掉多少个逆序对
}
rep (i, 1, m) {
ans[i] += ans[i - 1];
print (res - ans[i - 1], '\n');
}
return 0;
}
3.拦截导弹
d
p
l
[
i
]
dpl[i]
dpl[i]:以
i
i
i 为结尾的最长上升子序列长度。
d
p
r
[
i
]
dpr[i]
dpr[i]:以
i
i
i 为起点的最长上升子序列长度。
n
u
m
l
[
i
]
numl[i]
numl[i]:以
i
i
i 为起点的长度等于
d
p
l
[
i
]
dpl[i]
dpl[i] 的子序列的数量。
n
u
m
r
[
i
]
numr[i]
numr[i]:以
i
i
i 为起点的长度等于
d
p
r
[
i
]
dpr[i]
dpr[i] 的子序列的数量。
算出来后,总方案数为 ∑ i n u m l [ i ] ⋅ [ d p r [ i ] = 1 ] ⋅ [ d p l [ i ] = ∣ L I S ∣ ] \sum_{i} numl[i] \cdot [dpr[i] = 1] \cdot [dpl[i] = |LIS|] ∑inuml[i]⋅[dpr[i]=1]⋅[dpl[i]=∣LIS∣] (结尾为 i i i), i i i 的方案数为 n u m l [ i ] ∗ n u m r [ i ] ⋅ [ d p l [ i ] + d p r [ i ] − 1 = = ∣ L I S ∣ ] numl[i] * numr[i] \cdot [dpl[i] + dpr[i] - 1 == |LIS|] numl[i]∗numr[i]⋅[dpl[i]+dpr[i]−1==∣LIS∣]。
考虑怎么算这四个数组。
d p / n u m l dp/numl dp/numl和 d p / n u m r dp/numr dp/numr 计算方法类似,只讲 d p / n u m l dp/numl dp/numl。
d
p
[
i
]
=
max
(
d
p
[
j
]
)
(
h
[
j
]
≥
h
[
i
]
,
v
[
j
]
≥
v
[
i
]
)
dp[i] = \max (dp[j])(h[j] \geq h[i], v[j] \geq v[i])
dp[i]=max(dp[j])(h[j]≥h[i],v[j]≥v[i])
n
u
m
l
[
i
]
=
∑
j
n
u
m
l
[
j
]
(
n
u
m
l
[
j
]
)
(
h
[
j
]
≥
h
[
i
]
,
v
[
j
]
≥
v
[
i
]
,
d
p
[
j
]
+
1
=
d
p
[
i
]
)
numl[i] = \sum_{j} numl[j] (numl[j])(h[j] \geq h[i], v[j] \geq v[i],dp[j] + 1 = dp[i])
numl[i]=∑jnuml[j](numl[j])(h[j]≥h[i],v[j]≥v[i],dp[j]+1=dp[i])
做法:
先递归 [ l , m i d ] [l, mid] [l,mid],将 [ l , m i d ] [l, mid] [l,mid] 统给 [ m i d + 1 , r ] [mid + 1, r] [mid+1,r],然后递归 [ m i d + 1 , r ] [mid + 1, r] [mid+1,r]。
统给 [ m i d + 1 , r ] [mid + 1, r] [mid+1,r] 时:利用双指针搞定 h h h 的限制,接着利用权值线段树( v v v 为下标)求解,线段树每个节点记录 v v v 处于区间 [ l , r ] [l, r] [l,r] 的最长 L I S LIS LIS,以及它的方案数,然后区间查询 [ 1 , q [ j ] . v ] [1, q[j].v] [1,q[j].v] ,即为答案。
证明满足后效性:
类似归纳法:
正在计算区间 [ l , r ] [l, r] [l,r],此时 [ 1 , l − 1 ] [1,l - 1] [1,l−1] 一定已经转移完毕。
分裂成 [ l , m i d ] , [ m i d + 1 , r ] [l, mid], [mid+ 1, r] [l,mid],[mid+1,r]。
先递归求解 [ l , m i d ] [l, mid] [l,mid],这时 [ l , m i d ] [l, mid] [l,mid] 满足要求。
将 [ l , m i d ] [l, mid] [l,mid] 的贡献转移到 [ m i d + 1 , r ] [mid + 1, r] [mid+1,r] 后,再递归求解 [ m i d + 1 , r ] [mid + 1, r] [mid+1,r],这时 [ m i d + 1 , r ] [mid + 1, r] [mid+1,r] 被 [ 1 , m i d ] [1, mid] [1,mid] 修改过,所以 [ m i d + 1 , r ] [mid + 1, r] [mid+1,r] 也满足要求。
最初 [ 1 , 1 ] [1, 1] [1,1] 满足要求,得证。
参考代码
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define fi first
#define se second
#define db double
#define LL long long
#define ULL unsigned long long
#define PII pair <int, int>
#define PID pair <int, db>
#define MP(x,y) make_pair (x, y)
#define rep(i,j,k) for (int i = (j); i <= (k); ++i)
#define per(i,j,k) for (int i = (j); i >= (k); --i)
template <typename T> T Max (T x, T y) { return x > y ? x : y; }
template <typename T> T Min (T x, T y) { return x < y ? x : y; }
template <typename T> T Abs (T x) { return x > 0 ? x : -x; }
template <typename T>
void read (T &x) {
x = 0; T f = 1;
char ch = getchar ();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar ();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + ch - '0';
ch = getchar ();
}
x *= f;
}
template <typename T, typename... Args>
void read (T &x, Args&... args) {
read (x); read (args...);
}
char For_Print[25];
template <typename T>
void write (T x) {
if (x == 0) { putchar ('0'); return; }
if (x < 0) { putchar ('-'); x = -x; }
int poi = 0;
while (x) {
For_Print[++poi] = x % 10 + '0';
x /= 10;
}
while (poi) putchar (For_Print[poi--]);
}
template <typename T>
void print (T x, char ch) {
write (x); putchar (ch);
}
const LL Mod = 1e9 + 7;
LL square (LL x) { return (x * x) % Mod; }
void DEL (LL &x, LL y) { ((x -= y) < 0) && (x += Mod); }
void ADD (LL &x, LL y) { ((x += y) >= Mod) && (x -= Mod); }
const int Maxn = 2 * 1e5;
int n, cnt;
void Change (PID &x, PID y) {//(dpl[j],numl[j]) (y) 修改 (dpl[i], numl[i]) (x)
if (x.fi < y.fi) x = y;
else if (x.fi == y.fi) x.se += y.se;
}
#define ls (p << 1)
#define rs (p << 1 | 1)
#define lp (Tr[p].l)
#define rp (Tr[p].r)
#define midp ((lp + rp) >> 1)
#define lazyp (Tr[p].lazy)
#define resp (Tr[p].res)
struct Node {
int l, r;
bool lazy;
PID res;
};
struct Segment_Tree {
Node Tr[(Maxn << 2) + 5];
void Build (int p, int l, int r) {
lp = l; rp = r; lazyp = 0; resp = MP (0, 0);
if (lp == rp) {
return;
}
Build (ls, l, midp);
Build (rs, midp + 1, r);
}
void Change (int p) {//打上清空标记
lazyp = 1; resp = MP (0, 0);
}
void Change (PID &x, PID y) {//(dpl[j],numl[j]) (y) 修改 (dpl[i], numl[i]) (x)
if (x.fi < y.fi) x = y;
else if (x.fi == y.fi) x.se += y.se;
}
void Push_Up (int p) {//上传
resp = MP (0, 0);
Change (resp, Tr[ls].res);
Change (resp, Tr[rs].res);
}
void Push_Down (int p) {//清空懒惰标记
if (lazyp) {
Change (ls); Change (rs);
lazyp = 0;
}
}
void Update (int p, int Index, PID x) {//单点修改
if (lp == rp) {
Change (resp, x);
return;
}
Push_Down (p);
if (Index <= midp) Update (ls, Index, x);
else Update (rs, Index, x);
Push_Up (p);
}
PID Query (int p, int l, int r) {//区间查询
if (l <= lp && rp <= r) {
return resp;
}
Push_Down (p);
PID res = MP (0, 0);
if (l <= midp) Change (res, Query (ls, l, r));
if (r > midp) Change (res, Query (rs, l, r));
return res;
}
}Tree;
PII a[Maxn + 5];
int dpl[Maxn + 5], dpr[Maxn + 5], tmpdp[Maxn + 5];
db numl[Maxn + 5], numr[Maxn + 5], tmpnum[Maxn + 5];
struct Date {
int x, y, id;
PID dp;
// x 记录 h, y 记录 v,id 记录编号,dp 记录 dpl 和 numl
} q[Maxn + 5], tmp[Maxn + 5];
bool cmp (Date x, Date y) {
if (x.x != y.x) return x.x < y.x;
else return x.y < y.y;
}
void CDQ (int l, int r) {
if (l == r) return;
int mid = (l + r) >> 1;
CDQ (l, mid);
//首先递归 [l, mid]
rep (i, l, r) tmp[i] = q[i];
sort (q + l, q + mid + 1, cmp);
sort (q + mid + 1, q + r + 1, cmp);
int i = l, j = mid + 1, k = l;
while (i <= mid && j <= r) {
if (q[i].x <= q[j].x) {
Tree.Update (1, q[i].y, MP (q[i].dp.fi + 1, q[i].dp.se));
i++;
}
else {
Change (q[j].dp, Tree.Query (1, 0, q[j].y));//如题解所述
j++;
}
}
while (j <= r) { Change (q[j].dp, Tree.Query (1, 0, q[j].y)); j++; }
Tree.Change (1);//清空
for (k = l; k <= r; k++) {
tmpdp[q[k].id] = q[k].dp.fi;
tmpnum[q[k].id] = q[k].dp.se;
}
for (k = l; k <= r; k++) {
q[k] = tmp[k];
q[k].dp = MP (tmpdp[q[k].id], tmpnum[q[k].id]);
}
//还原
CDQ (mid + 1, r);
//继续递归 [mid + 1, r]
}
vector <int> lsh;
int Find (int x) {
return lower_bound (lsh.begin (), lsh.end (), x) - lsh.begin () + 1;
}
//离散化
int main () {
// freopen ("D:\\lihan\\1.in", "r", stdin);
// freopen ("D:\\lihan\\1.out", "w", stdout);
read (n); Tree.Build (1, 0, Maxn + 1);
rep (i, 1, n) {
read (a[i].fi, a[i].se);
lsh.push_back (a[i].fi);
lsh.push_back (a[i].se);
}
sort (lsh.begin (), lsh.end ()); lsh.erase (unique (lsh.begin (), lsh.end ()), lsh.end ());
rep (i, 1, n) {
a[i].fi = Find (a[i].fi);
a[i].se = Find (a[i].se);
}
cnt = 0;
q[++cnt] = { 0, 0, 0, MP (0, 1) };
per (i, n, 1)
q[++cnt] = { a[i].fi, a[i].se, i, MP (0, 1) };
//逆序要求前面(CDQ 中的顺序) 小于后面 ,直接赋值即可。
CDQ (1, cnt);
rep (i, 1, cnt) {
dpr[q[i].id] = q[i].dp.fi;
numr[q[i].id] = q[i].dp.se;
}
cnt = 0;
q[++cnt] = { 0, 0, 0, MP (0, 1) };
rep (i, 1, n)
q[++cnt] = { Maxn - a[i].fi, Maxn - a[i].se, i, MP (0, 1) };
//正序要求前面(CDQ 中的顺序)大于后面 ,所以用 $\infty - x/y$
CDQ (1, cnt);
rep (i, 1, cnt) {
dpl[q[i].id] = q[i].dp.fi;
numl[q[i].id] = q[i].dp.se;
}
PID res = MP (0, 0);
rep (i, 1, n)
if (dpr[i] == 1)//统计总共的方案数
Change (res, MP (dpl[i] + dpr[i] - 1, numl[i] * numr[i]));
print (res.fi, '\n');
rep (i, 1, n) {
if (dpl[i] + dpr[i] - 1 != res.fi) {
printf ("%.5f\n", (double)0);
//不能 printf ("%.5f\n", 0); 会挂,很奇妙
}
else {
printf ("%.5f\n", numl[i] * numr[i] / res.se);
//单个的方案数除以总方案数。
}
}
return 0;
}
4.序列
任意一个地方修改都满足,那么就是序列上任意一对相邻元素都满足修改后递增的要求,这个就是转移条件,写出状态转移即可。
d p [ i ] = max ( d p [ j ] + 1 ) ( r e a c h m a x [ j ] ≤ a [ i ] , a [ j ] ≤ r e a c h m i n [ i ] ) dp[i] = \max (dp[j] + 1) (reachmax[j] \leq a[i], a[j] \leq reachmin[i]) dp[i]=max(dp[j]+1)(reachmax[j]≤a[i],a[j]≤reachmin[i])
C D Q CDQ CDQ 优化 d p dp dp, 完了
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define fi first
#define se second
#define db double
#define LL long long
#define ULL unsigned long long
#define PII pair <int, int>
#define MP(x,y) make_pair (x, y)
#define rep(i,j,k) for (int i = (j); i <= (k); ++i)
#define per(i,j,k) for (int i = (j); i >= (k); --i)
template <typename T> T Max (T x, T y) { return x > y ? x : y; }
template <typename T> T Min (T x, T y) { return x < y ? x : y; }
template <typename T> T Abs (T x) { return x > 0 ? x : -x; }
template <typename T>
void read (T &x) {
x = 0; T f = 1;
char ch = getchar ();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar ();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + ch - '0';
ch = getchar ();
}
x *= f;
}
template <typename T, typename... Args>
void read (T &x, Args&... args) {
read (x); read (args...);
}
char For_Print[25];
template <typename T>
void write (T x) {
if (x == 0) { putchar ('0'); return; }
if (x < 0) { putchar ('-'); x = -x; }
int poi = 0;
while (x) {
For_Print[++poi] = x % 10 + '0';
x /= 10;
}
while (poi) putchar (For_Print[poi--]);
}
template <typename T>
void print (T x, char ch) {
write (x); putchar (ch);
}
const LL Mod = 1e9 + 7;
LL square (LL x) { return (x * x) % Mod; }
void DEL (LL &x, LL y) { ((x -= y) < 0) && (x += Mod); }
void ADD (LL &x, LL y) { ((x += y) >= Mod) && (x -= Mod); }
const int Maxn = 2 * 1e5;
int n, m;
int a[Maxn + 5], ans[Maxn + 5];
int reach_min[Maxn + 5], reach_max[Maxn + 5];
int BIT[Maxn + 5];
int lowbit (int x) { return x & -x; }
void Update (int Index, int x) {
for (int i = Index; i <= Maxn; i += lowbit (i))
BIT[i] = Max (BIT[i], x);
}
int Sum (int Index) {
int res = 0;
for (int i = Index; i >= 1; i -= lowbit (i))
res = Max (res, BIT[i]);
return res;
}
void Clean (int Index) {
for (int i = Index; i <= Maxn; i += lowbit (i))
BIT[i] = 0;
}
int cnt;
struct Date {
int op, x, y, id;
}q[Maxn + 5], tmp[Maxn + 5];
bool cmp (Date x, Date y) {
return x.x < y.x;
}
void CDQ (int l, int r) {
if (l == r) return;
int mid = (l + r) >> 1;
CDQ (l, mid);
rep (i, l, r) tmp[i] = q[i];
sort (q + l, q + mid + 1, cmp);
sort (q + mid + 1, q + r + 1, cmp);
int i = l, j = mid + 1, k = l;
while (i <= mid && j <= r) {
if (q[i].x <= q[j].x) {
if (q[i].op == 0)
Update (q[i].y, ans[q[i].id] + 1);
i++;
}
else {
if (q[j].op != 0)
ans[q[j].id] = Max (ans[q[j].id], Sum (q[j].y));
j++;
}
}
while (i <= mid) {
if (q[i].op == 0)
Update (q[i].y, ans[q[i].id] + 1);
i++;
}
while (j <= r) {
if (q[j].op != 0)
ans[q[j].id] = Max (ans[q[j].id], Sum (q[j].y));
j++;
}
i = l;
while (i <= mid) {
Clean (q[i].y);
i++;
}
for (i = l; i <= r; i++) q[i] = tmp[i];
CDQ (mid + 1, r);
}
int main () {
// freopen ("D:\\lihan\\1.in", "r", stdin);
// freopen ("D:\\lihan\\1.out", "w", stdout);
read (n, m);
rep (i, 1, n) {
read (a[i]);
reach_max[i] = reach_min[i] = a[i];
}
rep (i, 1, m) {
int x, y; read (x, y);
reach_max[x] = Max (reach_max[x], y);
reach_min[x] = Min (reach_min[x], y);
}
rep (i, 1, n) {
ans[i] = 1;
}
rep (i, 1, n) {
q[++cnt] = { 1, reach_min[i], a[i], i };
q[++cnt] = { 0, a[i], reach_max[i], i };
}
CDQ (1, cnt);
int res = 0;
rep (i, 1, n) {
res = Max (res, ans[i]);
}
cout << res;
return 0;
}
5.Building Bridges
f [ i ] = f [ j ] + ( h [ i ] − h [ j ] ) 2 + ( p r e [ i − 1 ] − p r e [ j ] ) 0 = f [ j ] − 2 h [ i ] h [ j ] + h 2 [ j ] − p r e [ j ] − f [ j ] − h 2 [ j ] + p r e [ j ] = − 2 h [ i ] h [ j ] f [ j ] + h 2 [ j ] − p r e [ j ] = 2 h [ i ] h [ j ] k = 2 h [ i ] y = f [ j ] + h 2 [ j ] − p r e [ j ] x = h [ j ] \begin{aligned} f[i] &= f[j] + (h[i] - h[j]) ^ 2 + (pre[i - 1] - pre[j]) \\ 0 &= f[j] - 2 h[i] h[j] + h^2[j] - pre[j] \\ -f[j] - h^2[j] + pre[j] &= -2h[i]h[j] \\ f[j] + h^2[j] - pre[j] &= 2h[i]h[j] \\ k &= 2h[i] \\ y &= f[j] + h ^ 2[j] - pre[j] \\ x &= h[j] \end{aligned} f[i]0−f[j]−h2[j]+pre[j]f[j]+h2[j]−pre[j]kyx=f[j]+(h[i]−h[j])2+(pre[i−1]−pre[j])=f[j]−2h[i]h[j]+h2[j]−pre[j]=−2h[i]h[j]=2h[i]h[j]=2h[i]=f[j]+h2[j]−pre[j]=h[j]
由于 X X X 不单调,结合例题三, C D Q CDQ CDQ 优化斜率优化 d p dp dp,完了。
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define fi first
#define se second
#define db double
#define LL long long
#define ULL unsigned long long
#define PII pair <int, int>
#define MP(x,y) make_pair (x, y)
#define rep(i,j,k) for (int i = (j); i <= (k); ++i)
#define per(i,j,k) for (int i = (j); i >= (k); --i)
template <typename T> T Max (T x, T y) { return x > y ? x : y; }
template <typename T> T Min (T x, T y) { return x < y ? x : y; }
template <typename T> T Abs (T x) { return x > 0 ? x : -x; }
template <typename T>
void read (T &x) {
x = 0; T f = 1;
char ch = getchar ();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar ();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + ch - '0';
ch = getchar ();
}
x *= f;
}
template <typename T, typename... Args>
void read (T &x, Args&... args) {
read (x); read (args...);
}
char For_Print[25];
template <typename T>
void write (T x) {
if (x == 0) { putchar ('0'); return; }
if (x < 0) { putchar ('-'); x = -x; }
int poi = 0;
while (x) {
For_Print[++poi] = x % 10 + '0';
x /= 10;
}
while (poi) putchar (For_Print[poi--]);
}
template <typename T>
void print (T x, char ch) {
write (x); putchar (ch);
}
const LL Mod = 1e9 + 7;
LL square (LL x) { return (x * x) % Mod; }
void DEL (LL &x, LL y) { ((x -= y) < 0) && (x += Mod); }
void ADD (LL &x, LL y) { ((x += y) >= Mod) && (x -= Mod); }
const int Maxn = 1e5;
int n;
LL h[Maxn + 5], w[Maxn + 5];
LL pre[Maxn + 5];
int cnt;
LL dp[Maxn + 5];
struct Node {
int id, id_cdq;
}q[Maxn + 5], tmp[Maxn + 5];
LL X (int j) {
return h[j];
}
LL Y (int j) {
return dp[j] + h[j] * h[j] - pre[j];
}
LL K (int i) {
return 2 * h[i];
}
LL DX (int j, int i) {
return X (i) - X (j);
}
LL DY (int j, int i) {
return Y (i) - Y (j);
}
LL Get_Dp (int j, int i) {
return dp[j] + pow (h[i] - h[j], 2) + (pre[i - 1] - pre[j]);
}
bool cmp (Node x, Node y) {
return K (x.id) < K (y.id);
}
int hh, tt, dullq[Maxn + 5];
bool check (int k, int j, int i) {
if (DX (j, i) == 0) {
return Y (j) >= Y (i);
}
else {
return DY (j, i) * DX (k, j) <= DY (k, j) * DX (j, i);
}
}
void add (int idx) {
while (hh <= tt - 1 && check (dullq[tt - 1], dullq[tt], idx))
tt--;
dullq[++tt] = idx;
}
void CDQ (int l, int r) {
if (l == r) return;
int mid = (l + r) >> 1;
int i = l, j = mid + 1, k;
rep (it, l, r) {
if (q[it].id_cdq <= mid) tmp[i++] = q[it];
else tmp[j++] = q[it];
}
rep (it, l, r)
q[it] = tmp[it];
CDQ (l, mid);
hh = 1; tt = 0;
rep (i, l, mid) add (q[i].id);
j = mid + 1;
while (hh + 1 <= tt && j <= r) {
if (DY (dullq[hh], dullq[hh + 1]) <= K (q[j].id) * DX (dullq[hh], dullq[hh + 1]))
hh++;
else {
dp[q[j].id] = Min (dp[q[j].id], Get_Dp (dullq[hh], q[j].id));
j++;
}
}
while (j <= r) {
dp[q[j].id] = Min (dp[q[j].id], Get_Dp (dullq[hh], q[j].id));
j++;
}
CDQ (mid + 1, r);
i = l; j = mid + 1; k = l;
while (i <= mid && j <= r)
if (X (q[i].id) <= X (q[j].id)) tmp[k++] = q[i++];
else tmp[k++] = q[j++];
while (i <= mid) tmp[k++] = q[i++];
while (j <= r) tmp[k++] = q[j++];
rep (it, l, r)
q[it] = tmp[it];
}
signed main () {
// freopen ("D:\\lihan\\1.in", "r", stdin);
// freopen ("D:\\lihan\\1.out", "w", stdout);
memset (dp, 0x3f, sizeof dp); dp[1] = 0;
read (n);
rep (i, 1, n)
read (h[i]);
rep (i, 1, n) {
read (w[i]);
pre[i] = pre[i - 1] + w[i];
}
rep (i, 1, n) q[++cnt].id = i, q[cnt].id_cdq = cnt;
sort (q + 1, q + 1 + cnt, cmp);
CDQ (1, n);
cout << dp[n];
return 0;
}
6.逝者
选 择 了 j , i ( j < i ) 的 贡 献 : a n s = max ( f a l l [ i ] ∗ n u m [ i ] − a t k [ i ] + f a l l [ j ] ∗ n u m [ j ] − a t k [ j ] − n u m [ j ] ∗ a t k [ i ] + p r e [ j − 1 ] ∗ a t k [ j ] + p r e [ i − 1 ] ∗ a t k [ i ] ) 0 = f a l l [ j ] ∗ n u m [ j ] − a t k [ j ] − n u m [ j ] ∗ a t k [ i ] + p r e [ j − 1 ] ∗ a t k [ j ] − f a l l [ j ] ∗ n u m [ j ] + a t k [ j ] − p r e [ j − 1 ] ∗ a t k [ j ] = − n u m [ j ] ∗ a t k [ i ] f a l l [ j ] ∗ n u m [ j ] + p r e [ j − 1 ] ∗ a t k [ j ] − a t k [ j ] = n u m [ j ] a t k [ i ] y = f a l l [ j ] ∗ n u m [ j ] + p r e [ j − 1 ] ∗ a t k [ j ] − a t k [ j ] , k = a t k [ i ] , x = n u m [ j ] \begin{aligned} 选择了 j,i (j < i) 的贡献: \\ ans = &\max (fall[i] * num[i] - atk[i] + fall[j] * num[j] - atk[j] - num[j] * atk[i] \\ & + pre[j - 1] * atk[j] + pre[i - 1] * atk[i]) \\ 0 = &fall[j] * num[j] - atk[j] - num[j] * atk[i] + pre[j - 1] * atk[j] \\ -fall[j] &* num[j] + atk[j] - pre[j - 1] * atk[j] = -num[j] * atk[i] \\ fall[j] * &num[j] + pre[j - 1] * atk[j] - atk[j] = num[j]atk[i] \\ y = fall[j] * &num[j] + pre[j - 1] * atk[j] - atk[j], k = atk[i], x = num[j] \end{aligned} 选择了j,i(j<i)的贡献:ans=0=−fall[j]fall[j]∗y=fall[j]∗max(fall[i]∗num[i]−atk[i]+fall[j]∗num[j]−atk[j]−num[j]∗atk[i]+pre[j−1]∗atk[j]+pre[i−1]∗atk[i])fall[j]∗num[j]−atk[j]−num[j]∗atk[i]+pre[j−1]∗atk[j]∗num[j]+atk[j]−pre[j−1]∗atk[j]=−num[j]∗atk[i]num[j]+pre[j−1]∗atk[j]−atk[j]=num[j]atk[i]num[j]+pre[j−1]∗atk[j]−atk[j],k=atk[i],x=num[j]
C D Q CDQ CDQ 优化斜率优化优化 d p dp dp 即可。
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define fi first
#define se second
#define db double
#define LL long long
#define ULL unsigned long long
#define PII pair <int, int>
#define MP(x,y) make_pair (x, y)
#define rep(i,j,k) for (int i = (j); i <= (k); ++i)
#define per(i,j,k) for (int i = (j); i >= (k); --i)
template <typename T> T Max (T x, T y) { return x > y ? x : y; }
template <typename T> T Min (T x, T y) { return x < y ? x : y; }
template <typename T> T Abs (T x) { return x > 0 ? x : -x; }
template <typename T>
void read (T &x) {
x = 0; T f = 1;
char ch = getchar ();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar ();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + ch - '0';
ch = getchar ();
}
x *= f;
}
template <typename T, typename... Args>
void read (T &x, Args&... args) {
read (x); read (args...);
}
char For_Print[25];
template <typename T>
void write (T x) {
if (x == 0) { putchar ('0'); return; }
if (x < 0) { putchar ('-'); x = -x; }
int poi = 0;
while (x) {
For_Print[++poi] = x % 10 + '0';
x /= 10;
}
while (poi) putchar (For_Print[poi--]);
}
template <typename T>
void print (T x, char ch) {
write (x); putchar (ch);
}
const LL Mod = 1e9 + 7;
LL square (LL x) { return (x * x) % Mod; }
void DEL (LL &x, LL y) { ((x -= y) < 0) && (x += Mod); }
void ADD (LL &x, LL y) { ((x += y) >= Mod) && (x -= Mod); }
const int Maxn = 3 * 1e5;
int n; LL myatk;
LL pre[Maxn + 5], fall[Maxn + 5];
struct Date {
LL atk, num;
}a[Maxn + 5];
bool cmp1 (Date x, Date y) {
return x.num * y.atk < x.atk * y.num;
}
int cnt;
LL dp[Maxn + 5];
struct Node {
int id, id_cdq;
}q[Maxn + 5], tmp[Maxn + 5];
LL X (int j) {
return a[j].num;
}
LL Y (int j) {
return fall[j] * a[j].num + pre[j - 1] * a[j].atk - a[j].atk;
}
LL K (int i) {
return a[i].atk;
}
LL DX (int j, int i) {
return X (i) - X (j);
}
LL DY (int j, int i) {
return Y (i) - Y (j);
}
LL Get_Dp (int j, int i) {
return fall[i] * a[i].num - a[i].atk + fall[j] * a[j].num - a[j].atk - a[j].num * a[i].atk + pre[j - 1] * a[j].atk + pre[i - 1] * a[i].atk;
}
bool cmp2 (Node x, Node y) {
return K (x.id) < K (y.id);
}
int hh, tt, dullq[Maxn + 5];
bool check (int k, int j, int i) {
if (DX (j, i) == 0) {
return Y (j) <= Y (i);
}
else {
return DY (j, i) * DX (k, j) >= DY (k, j) * DX (j, i);
}
}
void add (int idx) {
while (hh <= tt - 1 && check (dullq[tt - 1], dullq[tt], idx))
tt--;
dullq[++tt] = idx;
}
void CDQ (int l, int r) {
if (l == r) return;
int mid = (l + r) >> 1;
int i = l, j = mid + 1, k;
rep (it, l, r) {
if (q[it].id_cdq <= mid) tmp[i++] = q[it];
else tmp[j++] = q[it];
}
rep (it, l, r)
q[it] = tmp[it];
CDQ (l, mid);
hh = 1; tt = 0;
rep (i, l, mid) add (q[i].id);
j = mid + 1;
while (hh + 1 <= tt && j <= r) {
if (DY (dullq[hh], dullq[hh + 1]) >= K (q[j].id) * DX (dullq[hh], dullq[hh + 1]))
hh++;
else {
dp[q[j].id] = Max (dp[q[j].id], Get_Dp (dullq[hh], q[j].id));
j++;
}
}
while (j <= r) {
dp[q[j].id] = Max (dp[q[j].id], Get_Dp (dullq[hh], q[j].id));
j++;
}
CDQ (mid + 1, r);
i = l; j = mid + 1; k = l;
while (i <= mid && j <= r)
if (X (q[i].id) <= X (q[j].id)) tmp[k++] = q[i++];
else tmp[k++] = q[j++];
while (i <= mid) tmp[k++] = q[i++];
while (j <= r) tmp[k++] = q[j++];
rep (it, l, r)
q[it] = tmp[it];
}
signed main () {
// freopen ("D:\\lihan\\1.in", "r", stdin);
// freopen ("D:\\lihan\\1.out", "w", stdout);
read (n, myatk);
rep (i, 1, n) {
read (a[i].atk, a[i].num);
a[i].num = ceil ((a[i].num) * 1.0 / myatk);
}
sort (a + 1, a + 1 + n, cmp1);
per (i, n, 1) fall[i] = fall[i + 1] + a[i].atk;
rep (i, 1, n) pre[i] = pre[i - 1] + a[i].num;
rep (i, 1, n) q[++cnt] = { i, cnt };
sort (q + 1, q + 1 + cnt, cmp2);
CDQ (1, cnt);
LL _max = 0, res = 0;
rep (i, 1, n)
_max = Max (_max, dp[i]);
rep (i, 1, n) res += fall[i] * a[i].num - a[i].atk;
cout << res - _max;
return 0;
}
7.玩具装箱
d p [ i ] = d p [ j ] + ( p r e [ i ] − p r e [ j ] + i − j − 1 − L ) 2 使 p r e [ i ] + = i , L + + d p [ i ] = d p [ j ] + ( p r e [ i ] − p r e [ j ] − L ) 2 d p [ i ] = d p [ j ] + p r e [ i ] 2 + p r e [ j ] 2 + L 2 − 2 p r e [ i ] p r e [ j ] − 2 p r e [ i ] L + 2 p r e [ j ] L 0 = d p [ j ] + p r e [ j ] 2 − 2 p r e [ i ] p r e [ j ] + 2 p r e [ j ] L − p r e [ j ] 2 − 2 p r e [ j ] L = d p [ j ] − 2 p r e [ i ] p r e [ j ] p r e [ j ] 2 + 2 p r e [ j ] L + d p [ j ] = 2 p r e [ i ] p r e [ j ] y = p r e [ j ] 2 + 2 p r e [ j ] L + d p [ j ] k = 2 p r e [ i ] x = p r e [ j ] \begin{aligned} &dp[i] = dp[j] + (pre[i] - pre[j] + i - j - 1 - L) ^ 2 \\ &使 pre[i] += i, L++ \\ &dp[i] = dp[j] + (pre[i] - pre[j] - L) ^ 2 \\ &dp[i] = dp[j] + pre[i]^2 + pre[j]^2 + L^2 - 2pre[i]pre[j] - 2pre[i]L + 2pre[j]L \\ &0 = dp[j] + pre[j]^2 - 2pre[i]pre[j] + 2pre[j]L \\ &-pre[j]^2 - 2pre[j]L = dp[j] - 2pre[i]pre[j] \\ &pre[j]^2 + 2pre[j]L + dp[j] = 2pre[i]pre[j] \\ &y = pre[j]^2 + 2pre[j]L + dp[j] \\ &k = 2pre[i] \\ &x = pre[j] \end{aligned} dp[i]=dp[j]+(pre[i]−pre[j]+i−j−1−L)2使pre[i]+=i,L++dp[i]=dp[j]+(pre[i]−pre[j]−L)2dp[i]=dp[j]+pre[i]2+pre[j]2+L2−2pre[i]pre[j]−2pre[i]L+2pre[j]L0=dp[j]+pre[j]2−2pre[i]pre[j]+2pre[j]L−pre[j]2−2pre[j]L=dp[j]−2pre[i]pre[j]pre[j]2+2pre[j]L+dp[j]=2pre[i]pre[j]y=pre[j]2+2pre[j]L+dp[j]k=2pre[i]x=pre[j]
斜率优化
d
p
dp
dp, 但由于 正在学CDQ CDQ 斜率优化模板可以用于任何情况,所以用的是
C
D
Q
CDQ
CDQ 优化斜率优化优化
d
p
dp
dp。
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define fi first
#define se second
#define db double
#define LL long long
#define ULL unsigned long long
#define PII pair <int, int>
#define MP(x,y) make_pair (x, y)
#define rep(i,j,k) for (int i = (j); i <= (k); ++i)
#define per(i,j,k) for (int i = (j); i >= (k); --i)
template <typename T> T Max (T x, T y) { return x > y ? x : y; }
template <typename T> T Min (T x, T y) { return x < y ? x : y; }
template <typename T> T Abs (T x) { return x > 0 ? x : -x; }
template <typename T>
void read (T &x) {
x = 0; T f = 1;
char ch = getchar ();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar ();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + ch - '0';
ch = getchar ();
}
x *= f;
}
template <typename T, typename... Args>
void read (T &x, Args&... args) {
read (x); read (args...);
}
char For_Print[25];
template <typename T>
void write (T x) {
if (x == 0) { putchar ('0'); return; }
if (x < 0) { putchar ('-'); x = -x; }
int poi = 0;
while (x) {
For_Print[++poi] = x % 10 + '0';
x /= 10;
}
while (poi) putchar (For_Print[poi--]);
}
template <typename T>
void print (T x, char ch) {
write (x); putchar (ch);
}
const LL Mod = 1e9 + 7;
LL square (LL x) { return (x * x) % Mod; }
void DEL (LL &x, LL y) { ((x -= y) < 0) && (x += Mod); }
void ADD (LL &x, LL y) { ((x += y) >= Mod) && (x -= Mod); }
const int Maxn = 1e5;
int n; LL L;
LL pre[Maxn + 5];
int cnt;
LL dp[Maxn + 5];
int q[Maxn + 5], tmp[Maxn + 5];
LL X (int j) {
return pre[j];
}
LL Y (int j) {
return pre[j] * pre[j] + 2 * pre[j] * L + dp[j];
}
LL K (int i) {
return 2 * pre[i];
}
LL DX (int j, int i) {
return X (i) - X (j);
}
LL DY (int j, int i) {
return Y (i) - Y (j);
}
LL Get_Dp (int j, int i) {
return dp[j] + pow (pre[i] - pre[j] - L, 2);
}
bool cmp (int x, int y) {
return K (x) < K (y);
}
int hh, tt, dullq[Maxn + 5];
bool check (int k, int j, int i) {
if (DX (j, i) == 0) {
return Y (j) >= Y (i);
}
else {
return DY (j, i) * DX (k, j) <= DY (k, j) * DX (j, i);
}
}
void add (int idx) {
while (hh <= tt - 1 && check (dullq[tt - 1], dullq[tt], idx))
tt--;
dullq[++tt] = idx;
}
void CDQ (int l, int r) {
// printf ("l = %d, r = %d\n", l, r);
// if (l == r && q[l] == 13) {
// printf ("I Love NYH\n");
// }
// if (l == r && q[r] == 14) {
// printf ("I Love NYH\n");
// }
if (l == r) return;
int mid = (l + r) >> 1;
int i = l, j = mid + 1, k;
rep (it, l, r) {
if (q[it] + 1 <= mid) tmp[i++] = q[it];
else tmp[j++] = q[it];
}
rep (it, l, r)
q[it] = tmp[it];
CDQ (l, mid);
// if (l == 1 && r == 2) {
// printf ("I Love NYH\n");
// }
hh = 1; tt = 0;
rep (i, l, mid) add (q[i]);
j = mid + 1;
while (hh + 1 <= tt && j <= r) {
if (DY (dullq[hh], dullq[hh + 1]) <= K (q[j]) * DX (dullq[hh], dullq[hh + 1]))
hh++;
else {
dp[q[j]] = Min (dp[q[j]], Get_Dp (dullq[hh], q[j]));
j++;
}
}
while (j <= r) {
dp[q[j]] = Min (dp[q[j]], Get_Dp (dullq[hh], q[j]));
j++;
}
CDQ (mid + 1, r);
i = l; j = mid + 1; k = l;
while (i <= mid && j <= r)
if (X (q[i]) <= X (q[j])) tmp[k++] = q[i++];
else tmp[k++] = q[j++];
while (i <= mid) tmp[k++] = q[i++];
while (j <= r) tmp[k++] = q[j++];
rep (it, l, r)
q[it] = tmp[it];
}
signed main () {
// freopen ("D:\\lihan\\1.in", "r", stdin);
// freopen ("D:\\lihan\\1.out", "w", stdout);
memset (dp, 0x3f, sizeof dp);
read (n, L);
rep (i, 1, n)
read (pre[i]);
rep (i, 1, n)
pre[i] = pre[i - 1] + pre[i];
rep (i, 1, n)
pre[i] += i;
L++;
q[++cnt] = 0; dp[0] = 0;
rep (i, 1, n) q[++cnt] = i;
sort (q + 1, q + 1 + cnt, cmp);
CDQ (1, cnt);
// rep (i, 1, n) {
// printf ("dp[%d] = %lld\n", i, dp[i]);
// }
cout << dp[n];
return 0;
}