《CF833B The Bakery》

题目描述

不久前,Slastyona the Sweetmaid 决定开设自己的面包店!她买齐了所需的原料,还有一台可以烘焙多种蛋糕的奇妙烤箱,并开张了面包店。

很快,开支开始超过收入,于是 Slastyona 决定研究甜点市场。她了解到,将蛋糕装入盒子会更有利可图,而且每个盒子里不同种类蛋糕越多(我们将这个数量称为盒子的价值),价格就越高。

她需要改变生产技术!问题在于,烤箱会自行决定蛋糕的种类,Slastyona 无法干预。然而,她知道今天烤箱将要依次烘烤出 n 个蛋糕的类型和顺序。今天,Slastyona 必须准确地用 k 个盒子来打包这些蛋糕,并且每个盒子内必须放入若干(至少一个)连续烘焙出来的蛋糕(换句话说,每个盒子包含一段蛋糕的连续区间)。

Slastyona 希望最大化所有盒子的总价值。请你帮她求出所有盒子的最大可能总价值。

输入格式

第一行包含两个整数 n 和 k(1≤n≤35000,1≤k≤min(n,50))------蛋糕的数量以及盒子的数量。

第二行包含 n 个整数 a1​,a2​,...,an​(1≤ai​≤n)------按照烤箱生产顺序排列的蛋糕类型。

输出格式

输出唯一一个整数,表示所有盒子中的蛋糕最大可能的总价值。

显示翻译

题意翻译

输入输出样例

输入 #1复制

复制代码
4 1
1 2 2 1

输出 #1复制

复制代码
2

输入 #2复制

复制代码
7 2
1 3 3 1 4 4 4

输出 #2复制

复制代码
5

输入 #3复制

复制代码
8 3
7 7 8 7 7 8 1 7

输出 #3复制

复制代码
6

说明/提示

在第一个样例中,Slastyona 只有一个盒子。她必须把所有蛋糕都放进这个盒子里,因此这个盒子有两种类型,价值为 2。

在第二个样例中,最优策略是将前两个蛋糕放入第一个盒子,其余的都放进第二个盒子。则第一个盒子有两种类型,第二个盒子有三种类型,因此总价值为 5。

由 ChatGPT 5 翻译

代码实现;

cpp 复制代码
#include<bits/stdc++.h>
#define re register
#define il inline
using namespace std;

const int N=35005;
const int M=55;

int n,k;
int a[N];
int dp[N][M];
int pre[N], pos[N];
int vis[N], p[N];

struct SegTree{
    struct nd{
        int l,r,v,ad;
    }t[4*N];
    
    void pd(int p){
        if(t[p].ad){
            int lc=p<<1, rc=p<<1|1;
            t[lc].v += t[p].ad;
            t[rc].v += t[p].ad;
            t[lc].ad += t[p].ad;
            t[rc].ad += t[p].ad;
            t[p].ad = 0;
        }
    }
    
    void clr(int i, int p, int l, int r){
        t[p].l=l, t[p].r=r;
        if(l==r){
            t[p].v = dp[l][i];
            return ;
        }
        int m=(l+r)>>1;
        clr(i, p<<1, l, m);
        clr(i, p<<1|1, m+1, r);
        t[p].v = max(t[p<<1].v, t[p<<1|1].v);
    }
    
    void upd(int p, int l, int r, int x){
        if(t[p].l>=l && t[p].r<=r){
            t[p].ad += x;
            t[p].v += x;
            return ;
        }
        pd(p);
        int m=(t[p].l + t[p].r)>>1;
        if(l<=m) upd(p<<1, l, r, x);
        if(r>m) upd(p<<1|1, l, r, x);
        t[p].v = max(t[p<<1].v, t[p<<1|1].v);
    }
    
    int qry(int p, int l, int r){
        if(t[p].l>=l && t[p].r<=r) return t[p].v;
        pd(p);
        int m=(t[p].l + t[p].r)>>1, res=0;
        if(l<=m) res = qry(p<<1, l, r);
        if(r>m) res = max(res, qry(p<<1|1, l, r));
        return res;
    }
}tr[M];

int main(){
    scanf("%d %d", &n, &k);
    for(int i=1;i<=n;i++){
        scanf("%d", &a[i]);
        pre[i] = pos[a[i]];
        pos[a[i]] = i;
    }
    
    for(int i=1;i<=n;i++){
        dp[i][1] = dp[i-1][1];
        if(!vis[a[i]]){
            vis[a[i]] = 1;
            dp[i][1]++;
        }
    }
    
    for(int j=2;j<=k;j++){
        tr[j-1].clr(j-1, 1, 0, n);
        for(int i=1;i<=n;i++){
            tr[j-1].upd(1, pre[i], i-1, 1);
            dp[i][j] = tr[j-1].qry(1, 0, i-1);
        }
    }
    
    printf("%d", dp[n][k]);
    return 0;
}
相关推荐
散峰而望8 小时前
【算法练习】算法练习精选:陶陶摘苹果(基础+升级)、Music Notes、字串变换,你能AC几道?
数据结构·c++·算法·leetcode·贪心算法·github·动态规划
菜菜的顾清寒9 小时前
力扣HOT100(50)动态规划-零钱兑换
算法·leetcode·动态规划
晚笙coding9 小时前
从“看起来像双指针”到真正的动态规划 —— 最长公共子序列
算法·动态规划
随意起个昵称12 小时前
线性dp-计数类题目9(斐波那契字符串)
算法·动态规划
菜菜的顾清寒12 小时前
力扣HOT100(49)动态规划 -- 打家劫舍
算法·leetcode·动态规划
填满你的记忆12 小时前
《动态规划-基础篇》
算法·动态规划·力扣
散峰而望1 天前
【算法练习】算法练习精选:从 Phone numbers 到 Decrease,覆盖字符串、模拟、图论思维题
数据结构·c++·算法·贪心算法·github·动态规划·图论
阿文的代码库1 天前
递归与迭代的形式实现
算法·动态规划
拂拉氏2 天前
【知识讲解-题目讲解】算法系列之动态规划入门(上)
算法·leetcode·动态规划
随意起个昵称2 天前
区间dp-基础题目1(石子合并)
算法·动态规划