AT_arc180_d [ARC180D] Division into 3

题意:

给你一个长度为 \(N\) 的整数序列 \(A=(A_1,A_2,\cdots,A_N)\) 。请回答下列 \(Q\) 个问题。

  • \(i\) -th 查询:给定整数 \(L_i\)\(R_i\) 。求解下面 \(B=(A_{L_i},A_{L_i+1},\cdots,A_{R_i})\) 的问题。
    • \(B\) 分成三个非空的连续子序列。对于每个连续子序列,让我们找出其元素的最大值。求这些最大值之和的最小值。在这里,问题的限制条件迫使 \(B\) 的长度至少为 \(3\) ,因此总有至少一种方法将其分成三个非空的连续子序列。

分析:

\(A,B,C\) 分别表示依次划分的三段区间,对于整个序列的最大值 \(mx\),显然 \(A,B,C\) 的代价至少有一个等于 \(mx\)。分类讨论拆解这个问题。

\(A=mx\)

不妨假设 \(B\)\(C\) 的分界点被固定,因为 \(A\) 的代价已经确定,那么 \(B\) 长度越小,\(B\) 的代价就越小,总代价就越小。因此 \(B\) 的长度恰好为 \(1\)。记 \(x\) 表示 \(\min_{i=l}^{r}i[a_{i}=mx]\),那么总代价即为:

\[mx+\min_{i=x+1}^{r-1}(a_{i}+\max_{j=i+1}^{r}a_{j}) \]

\(f_{i}=a_{i}+\max_{j=i+1}^{r}a_{j}\),使用扫描线维护固定 \(r\) 时,所有的 \(f_{i}\)\(1\le i < r\))。

配合单调栈使用,当 \(r \leftarrow r+1\) 时,假设新加入单调栈的 \(a_{r}\) 弹出了 \(s_{top}\)\(a_{r}\ge a_{s_{top}}\)),那么在线段树上就是 \(f_{i} \leftarrow f_{i}-a_{s_{top}}+a_{r}\),其中 \(i \in [s_{top-1},s_{top})\)。然后再单独更新一下 \(f_{r-1}\)

\(B=mx\)

不难发现此时 \(A,C\) 的长度为 \(1\) 时,一定是最优的,因为 \(A\) 的左端点,\(B\) 的右端点都固定了,长度越小,代价越小。这是区间 RMQ 问题。ST 表或线段树就能解决。

\(C=mx\)

\(A=mx\) 是对称问题,序列翻转后再使用 \(A=mx\) 的算法即可。

时间复杂度 \(O((n+Q) \log n)\),代码虽然长但不用动脑子。

代码:

#include<bits/stdc++.h>
#define int long long
#define N 300005
using namespace std;

int n, Q;
int a[N], L[N], R[N], ans[N];

struct Tree1 { //区间加; 区间min 
	int c[N * 4], tag[N * 4];
	
	void clear() {
		memset(c, 0, sizeof(c));
		memset(tag, 0, sizeof(tag));
	}
	void pushup(int u) {
		c[u] = min(c[u * 2], c[u * 2 + 1]);
	}
	void maketag(int u, int x) {
		c[u] += x;
		tag[u] += x; 
	}
	void pushdown(int u) {
		if(!tag[u]) return;
		maketag(u * 2, tag[u]);
		maketag(u * 2 + 1, tag[u]);
		tag[u] = 0;
	}
	
	void update(int u, int L, int R, int l, int r, int x) {
		if(l <= L && R <= r) {
			maketag(u, x);
			return;
		}
		if(R < l || r < L) return;
		int mid = (L + R) / 2;
		pushdown(u);
		update(u * 2, L, mid, l, r, x);
		update(u * 2 + 1, mid + 1, R, l, r, x);
		pushup(u);
	}
	
	int query(int u, int L, int R, int l, int r) {
		if(l <= L && R <= r) return c[u];
		if(r < L || R < l) return 1e15;
		int mid = (L + R) / 2;
		pushdown(u);
		return min(query(u * 2, L, mid, l, r), query(u * 2 + 1, mid + 1, R, l, r));
	}
}t1;


struct node {
	int id, Max;
	friend node operator + (node A, node B) {
		if(A.Max > B.Max) return A;
		else if(A.Max < B.Max) return B;
		else return ((node){min(A.id, B.id), A.Max});
	}
};

struct Tree2 { //区间max 
	node c[N * 4];
	void pushup(int u) {
		c[u] = c[u * 2] + c[u * 2 + 1];
	}
	void build(int u, int L, int R) {
		if(L == R) {
			c[u].id = L;
			c[u].Max = a[L];
			return;
		}
		int mid = (L + R) /2;
		build(u * 2, L, mid);
		build(u * 2 + 1, mid + 1, R);
		pushup(u);
	}
	node query(int u, int L, int R, int l, int r) {
		if(l <= L && R <= r) return c[u];
		if(r < L || R < l) return (node){0, (int)-1e15};
		int mid = (L + R) / 2;
		return query(u * 2, L, mid, l, r) + query(u * 2 + 1, mid + 1, R, l, r);
	}
}t2;

vector<int>p[N];
int S[N], top;
void Sol1() { //解决 A=mx 或 C=mx 的问题 
	t1.clear();
	S[0] = 1; top = 0;
	for(int i = 1; i <= n; i++) p[i].clear();
	for(int i = 1; i <= Q; i++) p[R[i]].push_back(i);
	for(int i = 1; i <= n; i++) {
		while(top && a[i] >= a[S[top]]) {
			t1.update(1, 1, n, S[top - 1], S[top] - 1, -a[S[top]] + a[i]);
			top--;
		}
		S[++top] = i;
		if(i > 1) t1.update(1, 1, n, i - 1, i - 1, a[i - 1] + a[i]);
		for(auto Pos : p[i]) {
			node Get = t2.query(1, 1, n, L[Pos], R[Pos]);
			int x = Get.id;
			//cout << Get.Max << endl;
			//for(int z = x + 1; z <= i - 1; z++) cout << z << " : " << t1.query(1, 1, n, z, z) << endl;
			if(x + 1 <= i - 1) ans[Pos] = min(ans[Pos], Get.Max + t1.query(1, 1, n, x + 1, i - 1));
		}
	}
}


void Sol2() { //解决 B=mx 的问题 

	for(int i = 1; i <= Q; i++)
		ans[i] = min(ans[i], a[L[i]] + a[R[i]] + t2.query(1, 1, n, L[i] + 1, R[i] - 1).Max);
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	cin >> n >> Q;	
	for(int i = 1; i <= n; i++) cin >> a[i];
	for(int i = 1; i <= Q; i++) {
		cin >> L[i] >> R[i];
		ans[i] = 1e18;
	}
	
	
	t2.build(1, 1, n);
	
	Sol1();
	
	Sol2();
	
	reverse(a + 1, a + n + 1);
	for(int i = 1; i <= Q; i++) {
		int l = L[i], r = R[i];
		L[i] = n - r + 1;
		R[i] = n - l + 1;
	}
	t2.build(1, 1, n);
	Sol1();
	
	for(int i = 1; i <= Q; i++) cout << ans[i] << endl;
	return 0;
}
/*
5 1
4 3 1 1 4
1 5
*/

/*
7 1
4 3 1 1 4 5 2
1 7
*/
posted @ 2024-07-10 19:44  小超手123  阅读(10)  评论(0编辑  收藏  举报