[决策单调性] [莫队] CF868F Yet Another Minimization Problem 题解

posted on 2024-05-22 13:31:29 | under | source

容易得出转移 \(f_{i,j}=\min\limits_{0\le p\le i}(f_{p,j-1}+w(p+1,i))\)\(w\) 是区间价值。

显然满足决策单调性,即 \(j\) 不变 \(i\) 增加,则决策点不向左移。

用分治法处理。问题来了怎么算 \(w\)?现有的 \(\log\) 数据结构貌似不好处理,只能想莫队了。我们可以 \(O(1)\) 调整左右边界并得出相应价值。

然后卡在这一步了,主要是没有办法离线所有 \(w\),分治局限就在这,它强制要求转移的先后性了。

事实上,暴力移动即可。请先 yy 出一棵分治树,然后分析复杂度:

  1. \([l,r]\to [l,mid-1]\):移动次数不超过 \(r-l\)

  2. \([l,mid-1]\to [mid+1,r]\):考虑同一深度(同一层)内,左右边界不会向左移,总计 \(O(n)\)

所以做一次分治的总复杂度是 \(O(n\log n)\)

总结:

  1. 分治树其实满足许多优良性质。分析复杂度时可以将同一层的一起处理,因为这些区间不相交。且总层数 \(\log n\)

  2. 利用这些性质,可以在上面套一些复杂度只和区间大小相关的双指针算法(例如莫队)。

代码

#include<bits/stdc++.h>
using namespace std;

const int N = 2e5 + 5, K = 25;
int n, k, a[N], buc[N], f[N][K], s[N][K], opt[N][K], ql, qr, res;

inline int w(int l, int r){
	while(qr < r) res += buc[a[++qr]]++;
	while(ql > l) res += buc[a[--ql]]++;
	while(qr > r) res -= --buc[a[qr--]];
	while(ql < l) res -= --buc[a[ql++]];
	return res;
}
inline void calc(int l, int r, int kl, int kr, int j){
	if(l > r || kl > kr) return ;
	int k = kl, mid = l + r >> 1;
	for(int p = kl; p <= min(mid, kr); ++p){
		int W = f[p][j - 1] + w(p + 1, mid);
		if(W < f[mid][j]) k = p, f[mid][j] = W;
	}
	opt[mid][j] = k;
 	calc(l, mid - 1, kl, k, j), calc(mid + 1, r, k, kr, j);
}
signed main(){
	memset(f, 0x3f, sizeof f), f[0][0] = 0, ql = 1;
	cin >> n >> k;
	for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
	for(int j = 1; j <= k; ++j) calc(1, n, 0, n, j);
	cout << f[n][k];
	return 0;
}
posted @ 2026-01-12 20:16  Zwi  阅读(2)  评论(0)    收藏  举报