[决策单调性] [莫队] 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 出一棵分治树,然后分析复杂度:
-
\([l,r]\to [l,mid-1]\):移动次数不超过 \(r-l\)。
-
\([l,mid-1]\to [mid+1,r]\):考虑同一深度(同一层)内,左右边界不会向左移,总计 \(O(n)\)。
所以做一次分治的总复杂度是 \(O(n\log n)\)。
总结:
-
分治树其实满足许多优良性质。分析复杂度时可以将同一层的一起处理,因为这些区间不相交。且总层数 \(\log n\)。
-
利用这些性质,可以在上面套一些复杂度只和区间大小相关的双指针算法(例如莫队)。
代码
#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;
}

浙公网安备 33010602011771号