BCPC Winter Training5 TD

题意

长度n(35000)的数组,分成k(50)组,每组价值为数的种类数,问如何分组使总和最大。

思考

正常dp的话应该是dp[i][j]表示前j个人分成i段能获得的最大价值。

dp方程为

\[dp[i][j] = max_{k=i-1}^{j-1}\{dp[i - 1][k]+val(k+1,j)\} \]

时间复杂度是O(n2k)的,需要优化。

优化: 可以发现在同一个i中,我们的j是一个一个加进去的。而且,在更新一个j的时候val(i,j)的变化就是j位置上这个数到他上一个位置这个区间有了更新(原先这些数到最后一个数之间没有这个数,但现在有了,所有以最后一个数作为末端的区间都能加一价值)。这样的话,我们可以用线段树来维护等式右边。

枚举i,枚举j每次加入j然后更新线段树。

更新方法:

  1. 加入dp[i-1][j-1],单点修改。
  2. 更新val,将当前数上一个位置到当前j的区间价值+1。

查询最大值,作为dp[i][j]。

代码

#include <bits/stdc++.h>
using namespace std;
int n, m;
int a[35010];
int dp[55][35010];
int tree[35010 << 3];
int lazy[35010 << 3];
int pre[35010];
void reset()
{
    memset(tree, 0, sizeof(tree));
    memset(lazy, 0, sizeof(lazy));
}
void push_down(int x)
{
    tree[x << 1] += lazy[x];
    tree[x << 1 | 1] += lazy[x];
    lazy[x << 1] += lazy[x];
    lazy[x << 1 | 1] += lazy[x];
    lazy[x] = 0;
}
void add1(int num, int l, int r, int q, int c)
{
    if (l == r)
    {
        tree[num] += c;
        return ;
    }
    push_down(num);
    int mid = l + r >> 1;
    if (q <= mid)
        add1(num << 1, l, mid, q, c);
    else 
        add1(num << 1 | 1, mid + 1, r, q, c);
    tree[num] = max(tree[num << 1], tree[num << 1 | 1]);
}
void add2(int num, int l, int r, int ql,int qr, int c)
{
    if (ql <= l && r <= qr)
    {
        tree[num] += c;
        lazy[num] += c;
        return ;
    }
    push_down(num);
    int mid = l + r >> 1;
    if (ql <= mid)
        add2(num << 1, l, mid, ql, qr, c);
    if (qr > mid)
        add2(num << 1 | 1, mid + 1, r, ql, qr, c);
    tree[num] = max(tree[num << 1], tree[num << 1 | 1]);
}
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    for (int k = 1; k <= m; ++k)
    {
        reset();
        for (int i = 1; i <= n; ++i)
            pre[i] = k - 1;
        for (int i = k; i <= n; ++i)
        {
            add1(1, 0, n, i - 1, dp[k - 1][i - 1]);
            add2(1, 0, n, pre[a[i]], i - 1, 1);
            dp[k][i] = tree[1];
            pre[a[i]] = i;
        }
    }
    cout << dp[m][n];
    return 0;
}
posted @ 2022-02-27 10:27  cacu  阅读(16)  评论(0编辑  收藏  举报