Codeforces 833B 题解(DP+线段树)
题面
传送门: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+线段树)的更多相关文章
- Codeforces.833B.The Bakery(线段树 DP)
题目链接 \(Description\) 有n个数,将其分为k段,每段的值为这一段的总共数字种类,问最大总值是多少 \(Solution\) DP,用\(f[i][j]\)表示当前在i 分成了j份(第 ...
- Codeforces 1093G题解(线段树维护k维空间最大曼哈顿距离)
题意是,给出n个k维空间下的点,然后q次操作,每次操作要么修改其中一个点的坐标,要么查询下标为[l,r]区间中所有点中两点的最大曼哈顿距离. 思路:参考blog:https://blog.csdn.n ...
- Codeforces Round #620 F2. Animal Observation (hard version) (dp + 线段树)
Codeforces Round #620 F2. Animal Observation (hard version) (dp + 线段树) 题目链接 题意 给定一个nm的矩阵,每行取2k的矩阵,求总 ...
- [Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路)
[Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路) 题面 有n个空心物品,每个物品有外部体积\(out_i\)和内部体积\(in_i\),如果\(in_i& ...
- Codeforces Gym 100231B Intervals 线段树+二分+贪心
Intervals 题目连接: http://codeforces.com/gym/100231/attachments Description 给你n个区间,告诉你每个区间内都有ci个数 然后你需要 ...
- cf834D(dp+线段树区间最值,区间更新)
题目链接: http://codeforces.com/contest/834/problem/D 题意: 每个数字代表一种颜色, 一个区间的美丽度为其中颜色的种数, 给出一个有 n 个元素的数组, ...
- Buses and People CodeForces 160E 三维偏序+线段树
Buses and People CodeForces 160E 三维偏序+线段树 题意 给定 N 个三元组 (a,b,c),现有 M 个询问,每个询问给定一个三元组 (a',b',c'),求满足 a ...
- CodeForces 877E DFS序+线段树
CodeForces 877E DFS序+线段树 题意 就是树上有n个点,然后每个点都有一盏灯,给出初始的状态,1表示亮,0表示不亮,然后有两种操作,第一种是get x,表示你需要输出x的子树和x本身 ...
- ZOJ 3349 Special Subsequence 简单DP + 线段树
同 HDU 2836 只不过改成了求最长子串. DP+线段树单点修改+区间查最值. #include <cstdio> #include <cstring> #include ...
随机推荐
- windows javaee 安装
一. 下载jdk 并安装 二. 配置环境变量 JAVA_HOME:D:\Java\jdk1..0_25 CLASSPATH :.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\dt. ...
- 【03】Python 文件读写 JSON
1 打开文件 文件操作步骤: 1.打开文件获取文件的句柄,句柄就理解为这个文件 2.通过文件句柄操作文件 3.关闭文件. 1.1 打开方法 f = open('xxx.txt') #需f.close( ...
- 【UOJ#207】共价大爷游长沙
题目链接 题目描述 火车司机出秦川,跳蚤国王下江南,共价大爷游长沙.每个周末,勤劳的共价大爷都会开车游历长沙市. 长沙市的交通线路可以抽象成为一个 \(n\) 个点 \(n−1\) 条边的无向图,点编 ...
- kali安装docker以及配置阿里云镜像加速
1.需求 最近需要用到docker比较多,遂安装使用下,第一次用docker搭建测试环境,不得不说,docker真香.期间遇到了比较多奇奇怪怪的问题,网上的教程也比较多比较乱,遂记录一下. 2.安装d ...
- [REPRINT]Properties vs. Getters and Setters
http://www.python-course.eu/python3_properties.php Our new class means breaking the interface. The a ...
- 【NOIP2012模拟8.20】Memory
题目 话说某一天,CD在爬树的时候发现了树干上有一大串奇怪的符文,于是好奇的CD就从头到尾看了一遍.看完一遍以后CD觉得,最后一段文字好像很眼熟,好像在前面见过.对于CD来说,一开始看到的符文会印象特 ...
- 【usaco 2013 open yinyang】阴阳
题目 Farmer John 正在在计划自己的农场漫步.他的农场的结构就像一棵树:农场有N个谷仓(1<= N <=100,000),分别由N-1条路链接.这样,他便可以通过这些谷仓间的道路 ...
- 【leetcode】1146. Snapshot Array
题目如下: Implement a SnapshotArray that supports the following interface: SnapshotArray(int length) ini ...
- [luogu]P1066 2^k进制数[数学][递推][高精度]
[luogu]P1066 2^k进制数 题目描述 设r是个2^k 进制数,并满足以下条件: (1)r至少是个2位的2^k 进制数. (2)作为2^k 进制数,除最后一位外,r的每一位严格小于它右边相邻 ...
- 整体二分&cdq分治 ZOJ 2112 Dynamic Rankings
题目:单点更新查询区间第k大 按照主席树的思想,要主席树套树状数组.即按照每个节点建立主席树,然后利用树状数组的方法来更新维护前缀和.然而,这样的做法在实际中并不能AC,原因即卡空间. 因此我们采用一 ...