CF834D
题目链接:http://codeforces.com/contest/834/problem/D
题目大意:将一个有n个数的数列分成k段,每段的价值为该段中不同数字的个数,求k段的最大总价值。
解题思路:
思路来自叉姐 + GreenGrape
dp + segment trees.
dp不难想到。前 i 个数分成 j 段的最大价值:dp[i][j] = max( dp[i-1][k] + w(k+1,j), i-1 <= k < j). 但其实这样直接去搞的话分分钟TLE。
所以,我们需要使用线段树。详情请看代码,里面有个人的注释,请指教。
AC代码:
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <cstring>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <string>
#include <queue> using namespace std;
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
#define root 1 , N , 1
const int maxn=+;
int a[maxn],dp[][maxn],last[maxn];
int pre[maxn];
int tree[maxn<<],lazy[maxn<<]; //*****************************************************
//这一部分其实就是走模板
void pushup(int rt){
tree[rt]=max(tree[rt<<],tree[rt<<|]);
}
void pushdown(int rt){
if(lazy[rt]){
lazy[rt<<]+=lazy[rt];
lazy[rt<<|]+=lazy[rt];
tree[rt<<]+=lazy[rt];
tree[rt<<|]+=lazy[rt];
lazy[rt]=;
}
}
void update(int L,int R,int c,int l,int r,int rt){
if(L<=l&&r<=R){
lazy[rt]+=c;
tree[rt]+=c;
return;
}
pushdown(rt);
int m=(l+r)>>;
if(L<=m) update(L,R,c,lson);
if(m<R) update(L,R,c,rson);
pushup(rt);
}
int query(int L,int R,int l,int r,int rt){
if(L<=l&&r<=R)
return tree[rt];
pushdown(rt);
int m=(l+r)>>;
int ret=;
if(L<=m) ret=max(ret,query(L,R,lson));
if(m<R) ret=max(ret,query(L,R,rson));
return ret;
}
//******************************************************* int main(){
int n,k;
scanf("%d%d",&n,&k);
for(int i=;i<=n;i++){
scanf("%d",&a[i]);
pre[i]=last[a[i]];//pre[i]记录a[i]上一次出现的位置
last[a[i]]=i;
}
for(int i=;i<=k;i++){
memset(tree,,sizeof(tree));
memset(lazy,,sizeof(lazy));
for(int j=i-;j<=n;j++)
update(j,j,dp[i-][j],,n,);//把线段树各叶子结点的值初始化为dp[i-1][]的值,在dp[i][]这一维度上的操作其实就是在dp[i-1][]的基础上进行的。前面不能忘了把线段树的数据置0。
for(int j=i;j<=n;j++){
//对于以x为结尾(pre[j] <= x <= j-1)的dp[i-1][x],将a[j]作为第 i 段的结尾可以使得dp[i-1][x]对应的dp[i][j]的值+1。
//故此时线段树维护的就是max(dp[i-1][k] + w(k+1,j), i-1 <= k < j)。w(k+1,j)在这个更新的过程中逐次加和累积。实在是精妙无比。
update(pre[j],j-,,,n,);
dp[i][j]=query(,j,,n,);
}
}
int ans=;
for(int j=k;j<=n;j++){
if(dp[k][j]>ans) ans=dp[k][j];
}
printf("%d\n",ans);
return ;
}
CF834D的更多相关文章
- CF834D The Bakery
题目链接:戳我 题意:将一个长度为n的序列分为k段,使得总价值最大.一段区间的价值表示为区间内不同数字的个数 \(n<=35000,k<=50\) 开始想的转移方程是这个样子的--\(dp ...
- cf834D(dp+线段树区间最值,区间更新)
题目链接: http://codeforces.com/contest/834/problem/D 题意: 每个数字代表一种颜色, 一个区间的美丽度为其中颜色的种数, 给出一个有 n 个元素的数组, ...
随机推荐
- HDU Problem D [ Humble number ]——基础DP丑数序列
Problem D Time Limit : 2000/1000ms (Java/Other) Memory Limit : 65536/32768K (Java/Other) Total Submi ...
- 思维+模拟--POJ 1013 Counterfeit Dollar
Sally Jones has a dozen Voyageur silver dollars. However, only eleven of the coins are true silver d ...
- 图论--2-SAT--Tarjan连通分量+拓扑排序O(N+M)模板
#include <cstdio> #include <cstring> #include <queue> #include <vector> #inc ...
- Objective-C对象模型
Objective-C是一门面向对象的编程语言,每一个对象都是一个类的实例.XCode中打开objc.h可以看到如下定义: /// An opaque type that represents an ...
- IDEA破解,自动激活【2020年版本也可以破解】
破解 在破解之前,需要下载的文件有两个ideaIU-2019.3.3.exe.jetbrains-agent-latest.zip,前者是程序安装包,后者是破解补丁包(PS:补丁包里面有使用说明,下面 ...
- Redis 学习笔记(一) 字符串 SDS
SDS 简单动态字符串. SDS的结构: struct sdshdr{ int len;//记录BUF数组中已使用字节的数量 ,等于SDS所八寸字符串的长度 int free;//记录BUF数组中未使 ...
- linux centos7搭建redis-5.0.5
1. 下载redis 1.1 下载地址 http://download.redis.io/releases/ 1.2 安装版本 redis-5.0.5.tar.gz 2. 安装redis 2.1 前置 ...
- [hdu4714 Tree2cycle]树形DP
题意:给一棵树,删边和加边的代价都为1,求把树变成一个圈所花的最小代价. 思路:对原树进行删边操作,直到将原树分成若干条链,然后通过在链之间添加边形成圈,由于删边和加边一一对应,且最后需要额外一条边连 ...
- SpringData表关系:多对多
一.编写实体类配置关联关系: 1.多对多使用注解@ManyToMany配置:a. 在实体中添加一个集合属性 b.在属性上添加ManyToMany注解 c.@JoinTable 注解配置关联关系(nam ...
- 【clear linux】intel clear linux 电源状态命令
# 重启系统 $ sudo systemctl reboot # 关闭系统,切断电源 $ sudo systemctl poweroff # CPU停止工作 $ sudo systemctl halt ...