題目鏈接:點我啊╭(╯^╰)╮
題目大意:
個數字,分爲 段
使每段內相同數字的對數 的和最小
解題思路:
爲以 爲止,分爲 段的最小值
發現隨着 的增大,滿足決策單調性
設區間 的答案位置在
則可以進行類似整體二分的轉移
對於修改,可以用莫隊處理,複雜度均攤
核心:決策單調在整體二分上的應用
#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
using pii = pair <int,int>;
const int maxn = 1e5 + 5;
int n, k, a[maxn], nl = 1, nr;
ll dp[maxn][25], cnt[maxn], sum;
inline void add(int x){
x = a[x];
if(cnt[x]) sum -= cnt[x] * (cnt[x] - 1) / 2;
cnt[x]++;
if(cnt[x]) sum += cnt[x] * (cnt[x] - 1) / 2;
}
inline void del(int x){
x = a[x];
if(cnt[x]) sum -= cnt[x] * (cnt[x] - 1) / 2;
cnt[x]--;
if(cnt[x]) sum += cnt[x] * (cnt[x] - 1) / 2;
}
inline void gao(int ql, int qr){
while(ql < nl) add(--nl);
while(ql > nl) del(nl++);
while(qr < nr) del(nr--);
while(qr > nr) add(++nr);
}
void solve(int l, int r, int x, int y, int k){
if(l > r || x > y) return;
int mid = x + y >> 1, pos;
ll mi = 1e18;
for(int i=l; i<=min(mid, r); i++){
gao(i, mid);
ll tmp = dp[i-1][k-1] + sum;
if(tmp < mi){
dp[mid][k] = mi = tmp;
pos = i;
}
}
solve(l, pos, x, mid-1, k);
solve(pos, r, mid+1, y, k);
}
int main() {
scanf("%d%d", &n, &k);
for(int i=1; i<=n; i++) scanf("%d", a+i);
for(int i=1; i<=n; i++)
gao(1, i), dp[i][0] = sum;
for(int i=1; i<k; i++)
solve(1, n, 1, n, i);
printf("%lld\n", dp[n][k-1]);
}