【学习笔记】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,l1] 一定已经转移完毕。

分裂成 [ 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]0f[j]h2[j]+pre[j]f[j]+h2[j]pre[j]kyx=f[j]+(h[i]h[j])2+(pre[i1]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[j1]atk[j]+pre[i1]atk[i])fall[j]num[j]atk[j]num[j]atk[i]+pre[j1]atk[j]num[j]+atk[j]pre[j1]atk[j]=num[j]atk[i]num[j]+pre[j1]atk[j]atk[j]=num[j]atk[i]num[j]+pre[j1]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]+ij1L)2使pre[i]+=i,L++dp[i]=dp[j]+(pre[i]pre[j]L)2dp[i]=dp[j]+pre[i]2+pre[j]2+L22pre[i]pre[j]2pre[i]L+2pre[j]L0=dp[j]+pre[j]22pre[i]pre[j]+2pre[j]Lpre[j]22pre[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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值