zoukankan      html  css  js  c++  java
  • [Codeforces 868F]Yet Another Minimization Problem

    Description

    题库链接

    给定一个序列 (a),要把它分成 (k) 个子段。每个子段的费用是其中相同元素的对数。求所有子段的费用之和的最小值。

    (2 leq n leq 10^5,2 leq k leq min(n,20),1 leq a_i leq n)

    Solution

    (f_{i,j}) 表示 (1sim i) 分为 (j) 段的最小费用。

    那么 (f_{i,j}=minlimits_{1leq kleq i}{f_{k-1,j-1}+w(k,i)})。其中 (w(l,r)) 表示区间 ([l,r]) 这一段的费用。

    并且可以知道 (forall a<b<c<d,w(a,d)+w(b,c)geq w(a,c)+w(b,d))。证明的话可以对每个不同的数 (a) 进行考虑,假设 (asim b) 这段区间 (a)(x) 个,(bsim c) 中有 (y) 个,(csim d) 中有 (z) 个。然后可知 (w(a,d)=frac{(1+x+y+z)(x+y+z)}{2}),其余同理,之后暴力拆开可以直接证明。

    满足四边形不等式后那么就可以在 (O(nlog n)) 的时间内解决了。并且由于 (w(i,j)) 运算复杂度较高,因此我们考虑分治。设 ([l,r]) 是决策区间,([L,R]) 是更新区间。

    在分治的过程中计算 (w)。由于每层重新清空重新计算 (w) 的复杂度是伪的,具体是因为如果把 (rsim MID(MID=(L+R)/2)) 之间的数扫一遍的话复杂度是错的。

    因此我们可以模拟莫队的操作只变更端点,这样复杂度可以保证,这是因为保存值的区间的左端点是随着 ([l,r]) 变化的,右端点是随着 ([L,R]) 变化的。每个变化都能保证复杂度是 (O(nlog n))

    Code

    #include <bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int N = 1e5+5;
    
    int n, k, a[N], cnt[N], nl = 1, nr;
    ll g[2][N], ans;
    
    void cdq(int l, int r, int L, int R) {
        int MID = (L+R)>>1, mid, loc = MID, pl = nl, pr = nr;
        if (r < MID) loc = r;
        mid = loc;
        while (nr < MID) ans += cnt[a[++nr]]++;
        while (nl > loc) ans += cnt[a[--nl]]++;
        while (nr > MID) ans -= --cnt[a[nr--]];
        while (nl < loc) ans -= --cnt[a[nl++]];
        g[1][MID] = g[0][loc-1]+ans;
        for (int i = loc-1; i >= l; i--) {
            ans += cnt[a[i]]++;
            if (g[0][i-1]+ans < g[1][MID])
                mid = i, g[1][MID] = g[0][i-1]+ans;
        }
        nl = l;
        if (L < MID) cdq(l, mid, L, MID-1);
        if (MID < R) cdq(mid, r, MID+1, R);
        while (nr < pr) ans += cnt[a[++nr]]++;
        while (nl > pl) ans += cnt[a[--nl]]++;
        while (nr > pr) ans -= --cnt[a[nr--]];
        while (nl < pl) ans -= --cnt[a[nl++]];
    }
    int main() {
        scanf("%d%d", &n, &k);
        for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
        memset(g[0], 127/3, sizeof(g[0]));
        g[0][0] = 0;
        while (k--) {
            cdq(1, n, 1, n);
            swap(g[0], g[1]);
        }
        printf("%lld
    ", g[0][n]);
        return 0;
    }
  • 相关阅读:
    hdu 4614 线段树 二分
    cf 1066d 思维 二分
    lca 最大生成树 逆向思维 2018 徐州赛区网络预赛j
    rmq学习
    hdu 5692 dfs序 线段树
    dfs序介绍
    poj 3321 dfs序 树状数组 前向星
    cf 1060d 思维贪心
    【PAT甲级】1126 Eulerian Path (25分)
    【PAT甲级】1125 Chain the Ropes (25分)
  • 原文地址:https://www.cnblogs.com/NaVi-Awson/p/12342008.html
Copyright © 2011-2022 走看看