题面

传送门:http://codeforces.com/problemset/problem/833/B
B. The Bakery
time limit per test2.5 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output

Some time ago Slastyona the Sweetmaid decided to open her own bakery! She bought required ingredients and a wonder-oven which can bake several types of cakes, and opened the bakery.

Soon the expenses started to overcome the income, so Slastyona decided to study the sweets market. She learned it’s profitable to pack cakes in boxes, and that the more distinct cake types a box contains (let’s denote this number as the value of the box), the higher price it has.

She needs to change the production technology! The problem is that the oven chooses the cake types on its own and Slastyona can’t affect it. However, she knows the types and order of n cakes the oven is going to bake today. Slastyona has to pack exactly k boxes with cakes today, and she has to put in each box several (at least one) cakes the oven produced one right after another (in other words, she has to put in a box a continuous segment of cakes).

Slastyona wants to maximize the total value of all boxes with cakes. Help her determine this maximum possible total value.

Input
The first line contains two integers n and k (1 ≤ n ≤ 35000, 1 ≤ k ≤ min(n, 50)) – the number of cakes and the number of boxes, respectively.

The second line contains n integers a1, a2, …, an (1 ≤ ai ≤ n) – the types of cakes in the order the oven bakes them.

Output
Print the only integer – the maximum total value of all boxes with cakes.

Examples
inputCopy
4 1
1 2 2 1
outputCopy
2
inputCopy
7 2
1 3 3 1 4 4 4
outputCopy
5
inputCopy
8 3
7 7 8 7 7 8 1 7
outputCopy
6
Note
In the first example Slastyona has only one box. She has to put all cakes in it, so that there are two types of cakes in the box, so the value is equal to 2.

In the second example it is profitable to put the first two cakes in the first box, and all the rest in the second. There are two distinct types in the first box, and three in the second box then, so the total value is 5.

题目大意:
有一个长度为n的序列,将它分为k段,定义每段的值为该段中不重复元素的个数,求k段值之和的最大值
如:n=4,k=2
1 2 2 1
可分为1,2和2,1两段,值之和为4

分析

1.DP方程的推导

设dp[i][j]dp[i][j]表示前j个数分为i段的最大值
状态转移方程为:
dp[i][j]=max(dp[i][j],dp[i−1][x−1]+num[x][j])dp[i][j]=max(dp[i][j],dp[i−1][x−1]+num[x][j])
其中x∈[i,j]x∈[i,j], num[x][j]num[x][j]表示区间[x,j][x,j]中不同的数的个数
dp本身的时间复杂度为O(n2k)O(n2k),遍历数组求num的时间复杂度为O(n)O(n),总时间复杂度为O(n3k)O(n3k)

2.求不同数个数的优化

设上一个等于a[i]的数的位置为last[i](如果是第一次,则last[i]=0)last[i](如果是第一次,则last[i]=0)
那么
处理到a[j]时,设x从last[j]+1到j,从last[i]+1到j的num[x][j]值均要加1
这是因为从last[i]+1到j的数一定没有a[j],加入a[j]后,不同的数个数一定多了1个
如:
a: 1 2 2 1
last: 0 0 2 1
如果不明白的话,可以在纸上模拟一下过程,但要确保理解上面这句话

3.线段树的优化

我们将状态转移方程中的dp[i−1][x−1]+num[x][j]dp[i−1][x−1]+num[x][j]看作一个整体,发现可以用线段树维护[i,j]最大值,这样只要一次查询就能求出max
另外,从last[i]+1到i的num[x][i]值均要加1这一操作,可以用区间修改来实现
所以我们只要枚举i从1到k,
每次对上一次的dp值(dp[i-1])建树
再枚举j从i到n,用区间修改,然后查询就能求出dp[i][j]的值
建树复杂度O(nlog2n)O(nlog2n)
n次修改和查询时间复杂度为O(nlog2n)O(nlog2n)
总时间复杂度为O(nklog2n)O(nklog2n)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 35005
#define maxk 55
using namespace std;
int n,k;
int a[maxn];
int hash[maxn],last[maxn];
int dp[maxk][maxn]; //dp[i][j]表示前j个数分成i段的方法数
struct node{
int l;
int r;
int v;
int mark;
}tree[maxn<<2];
void push_up(int pos){
tree[pos].v=max(tree[pos<<1].v,tree[pos<<1|1].v);
}
void build(int i,int l,int r,int pos){
tree[pos].l=l;
tree[pos].r=r;
tree[pos].mark=0;
if(l==r){
tree[pos].v=dp[i][l-1];
return;
}
int mid=(l+r)>>1;
build(i,l,mid,pos<<1);
build(i,mid+1,r,pos<<1|1);
push_up(pos);
}
void push_down(int pos){
if(tree[pos].mark!=0){
tree[pos<<1].mark+=tree[pos].mark;
tree[pos<<1|1].mark+=tree[pos].mark;
tree[pos<<1].v+=tree[pos].mark;
tree[pos<<1|1].v+=tree[pos].mark;
tree[pos].mark=0;
}
}
void update(int L,int R,int v,int pos){
if(L<=tree[pos].l&&R>=tree[pos].r){
tree[pos].v+=v;
tree[pos].mark+=v;
return;
}
push_down(pos);
int mid=(tree[pos].l+tree[pos].r)>>1;
if(L<=mid)update(L,R,v,pos<<1);
if(R>mid)update(L,R,v,pos<<1|1);
push_up(pos);
}
int query(int L,int R,int pos){
if(L<=tree[pos].l&&R>=tree[pos].r){
return tree[pos].v;
}
push_down(pos);
int mid=(tree[pos].l+tree[pos].r)>>1;
int ans=0;
if(L<=mid)ans=max(ans,query(L,R,pos<<1));
if(R>mid) ans=max(ans,query(L,R,pos<<1|1));
return ans;
}
int main(){
scanf("%d %d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
last[i]=hash[a[i]];//last[i]->a[i]上一次出现的位置
hash[a[i]]=i;//hash[x]-> x上一次出现的位置
}
//dp[i][j]=max(dp[i-1][x-1]+num[x][j],dp[i][j]) (i<=x<=j,num[x][j]->[x,j]不同数的个数
//线段树维护 dp[i-1][x-1]+num[x][j],变成区间查询最大值问题
//加入a[j],从last[j]+1到j的所有num值都要+1 ,用线段树区间更新
for(int i=1;i<=k;i++){
build(i-1,1,n,1);
for(int j=i;j<=n;j++){
update(last[j]+1,j,1,1);
dp[i][j]=query(i,j,1);
}
}
printf("%d\n",dp[k][n]);
}

Codeforces 833B 题解(DP+线段树)的更多相关文章

  1. Codeforces.833B.The Bakery(线段树 DP)

    题目链接 \(Description\) 有n个数,将其分为k段,每段的值为这一段的总共数字种类,问最大总值是多少 \(Solution\) DP,用\(f[i][j]\)表示当前在i 分成了j份(第 ...

  2. Codeforces 1093G题解(线段树维护k维空间最大曼哈顿距离)

    题意是,给出n个k维空间下的点,然后q次操作,每次操作要么修改其中一个点的坐标,要么查询下标为[l,r]区间中所有点中两点的最大曼哈顿距离. 思路:参考blog:https://blog.csdn.n ...

  3. Codeforces Round #620 F2. Animal Observation (hard version) (dp + 线段树)

    Codeforces Round #620 F2. Animal Observation (hard version) (dp + 线段树) 题目链接 题意 给定一个nm的矩阵,每行取2k的矩阵,求总 ...

  4. [Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路)

    [Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路) 题面 有n个空心物品,每个物品有外部体积\(out_i\)和内部体积\(in_i\),如果\(in_i& ...

  5. Codeforces Gym 100231B Intervals 线段树+二分+贪心

    Intervals 题目连接: http://codeforces.com/gym/100231/attachments Description 给你n个区间,告诉你每个区间内都有ci个数 然后你需要 ...

  6. cf834D(dp+线段树区间最值,区间更新)

    题目链接: http://codeforces.com/contest/834/problem/D 题意: 每个数字代表一种颜色, 一个区间的美丽度为其中颜色的种数, 给出一个有 n 个元素的数组, ...

  7. Buses and People CodeForces 160E 三维偏序+线段树

    Buses and People CodeForces 160E 三维偏序+线段树 题意 给定 N 个三元组 (a,b,c),现有 M 个询问,每个询问给定一个三元组 (a',b',c'),求满足 a ...

  8. CodeForces 877E DFS序+线段树

    CodeForces 877E DFS序+线段树 题意 就是树上有n个点,然后每个点都有一盏灯,给出初始的状态,1表示亮,0表示不亮,然后有两种操作,第一种是get x,表示你需要输出x的子树和x本身 ...

  9. ZOJ 3349 Special Subsequence 简单DP + 线段树

    同 HDU 2836 只不过改成了求最长子串. DP+线段树单点修改+区间查最值. #include <cstdio> #include <cstring> #include ...

随机推荐

  1. WPF Geometry="M0,0 L1,0 1,0.1, 0,0.1Z" 画方格背景图

    此项目源码下载地址:https://github.com/lizhiqiang0204/Tile 方格效果: 前端代码如下: <Window x:Class="WpfApp1.Main ...

  2. springboot中常用注解总结

    1.@RestController(组合注解):标注在类上,等价于@Controller和@Responsebody @Controller:将该类标记为Controller层的类,并且注入到Spri ...

  3. kettle中使用mysql的tinyint 类型到slqserver的tinyint类型

    各个数据库之间的类型 定义还是有差别的 一下是我在工作中遇到的一个很奇葩的问题  mysql 中的 tinyint 类型 插入到sqlserver 的tinyint 类型 插入到 sqlserver的 ...

  4. Linux 安装R包

    https://www.cnblogs.com/jessepeng/p/10984983.html Linux 的R环境,可以通过anaconda jupyter notbook很容易的配置,见我之前 ...

  5. 解决Acunetix 12中文汉化的方法

    最近下载一款测试软件acunetix,苦于满屏英文的苦恼,看不懂,于是乎就问度娘,结果度娘就是给中文破解包: 我是12版的,网上提供的都是11版的,没法用.怎么办呢?还好我是做测试的,平时做兼容性测试 ...

  6. [HDU6403]:Card Game(dfs+DP+基环树)

    题目传送门 题目描述 她依然在我不知道的地方做我不知道的事.桌面上摊开着一些卡牌,这是她平时很爱玩的一个游戏.如今卡牌还在,她却不在我身边.不知不觉,我翻开了卡牌,回忆起了当时一起玩卡牌的那段时间.每 ...

  7. eclipse jshint安装

    http://www.cnblogs.com/lhb25/archive/2012/04/05/javascript-validation-with-jshint-eclipse.html

  8. WebServices 实现跨应用程序进行通信和跨平台进行通信

    SOA ,即Service Oriented Architecture ,中文一般理解为面向服务的架构, 既然说是一种架构的话,所以一般认为 SOA 是包含了运行环境,编程模型, 架构风格和相关方法论 ...

  9. 力扣60——第k个排列

    原题 给出集合 [1,2,3,-,n],其所有元素共有 n! 种排列. 按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下: 1. "123" 2. &qu ...

  10. java的基本数据类型有

    整型数据根据它所占内容大小的不同可分为4种类型. 数据类型 内存 byte 8位 short 16位 int 32位 long 64位 浮点类型 数据类型 内存 float 32位 double 64 ...