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;
    }
  • 相关阅读:
    二、DBMS_JOB(用于安排和管理作业队列)
    Oracle 常用系统包
    DBMS_OUTPUT(用于输入和输出信息)
    C#获取当前主机硬件信息
    Centos安装mysql5.7
    Rsync安装和配置
    利用Docker编译Hadoop 3.1.0
    hadoop集群环境搭建
    axios请求、拦截器
    import时,什么时候使用花括号'{ }'
  • 原文地址:https://www.cnblogs.com/NaVi-Awson/p/12342008.html
Copyright © 2011-2022 走看看