题面

传送门: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. font-awesome样式只显示方框

    这是一个踩过的坑:使用font-awesome中的css样式库时,比如fa-user-circle-o,显示的不是一个用户图标,而是一个方框. 怎么回事呢? 进入css文件,发现: 咦,这些文件呢?我 ...

  2. 前端之CSS:属性操作2

    css样式之属性操作 一.文本属性 1.text-align:cnter 文本居中2.line heigth 垂直居中 :行高,和高度对应3.设置图片与文本的距离:vertical-align4.te ...

  3. re模块下的的常用方法

    引入模块: import re 1.查找findall   匹配所有,每一项都是列表中的一个元素 ret=re.findall("\d+","sjkhk172按实际花费9 ...

  4. 【LuoguP3264】[JLOI2015] 管道连接(斯坦那树)

    题目链接 题目描述 小铭铭最近进入了某情报部门,该部门正在被如何建立安全的通道连接困扰.该部门有 n 个情报站,用 1 到 n 的整数编号.给出 m 对情报站 ui;vi 和费用 wi,表示情报站 u ...

  5. SQL插入字段

    //SQL插入字段 String dropTable="drop table if exists test;"; String columnGid ="alter tab ...

  6. linux运维、架构之路-LVS负载均衡

    一.LVS介绍 1.介绍           LVS是Linux Virtual Server的简写,是linux虚拟的服务器集群系统,可以在unix/linux平台下实现负载均衡集群功能,由章文嵩博 ...

  7. Javascript面试题解析

    Javascript的一些面试题让很多同学感到头疼,下面就根据兄弟连教育 )毕业学员面试遇到的面试题,给大家做一个简单的分享,希望对初入职场的你们有一些帮助:Javascript面试题解析. 第一题 ...

  8. HDU 6206 Apple ( 高精度 && 计算几何 && 三点构圆求圆心半径 )

    题意 : 给出四个点,问你第四个点是否在前三个点构成的圆内,若在圆外输出"Accepted",否则输出"Rejected",题目保证前三个点不在一条直线上. 分 ...

  9. shell时间转换脚本

    字符串转换为时间戳: time2utc #!/bin/sh Time=$ date -d "${Time}" '+%s' 时间戳转日期字符串 utc2time #!/bin/sh ...

  10. Gradle教程-w3cschool

    Gradle,这是一个基于 JVM 的富有突破性构建工具.Gradle 正迅速成为许多开源项目和前沿企业构建系统的选择,同时也在挑战遗留的自动化构建项目.本教程主要讲解了如何使用 Gradle 构建系 ...