http://codeforces.com/problemset/problem/834/D

将一个长度为n的序列分为k段

使得总价值最大一段区间的价值表示为区间内不同数字的个数

n<=35000,k<=50

这题的dp是十分显然的,用dp[i][j]表示前i个数字分成j段的最大值

状态转移方程就是 dp[i][j] = max(dp[i - 1][k] + dis[k + 1][j] | (0 <= k <= j - 1))

但是乍一想可能是个O(knn)的算法,仔细一想果然是。

考虑线段树的优化。显然根据状态转移方程,线段树维护的是dp[i - 1][k] + dis[k + 1][j]的最大值

也就是对每一层都build一次线段树,然后以此查询更新,时间复杂度是O(knlnn),看着十分科学

其中一个难点在于对颜色种类的维护,dis这个35k * 35k的数组肯定是存不下的,仔细一想其实不需要什么花里胡哨的操作,用一个pre数组存储上一个这个颜色出现的地方,当遇到这个颜色的时候,在线段树pre[x] + 1,x的位置全部加上1,意在pre[x] + 1到x出现了这个颜色,种类++;

然后整个思路就出来了,对于每一层状态,用一个build更新上一层的dp[i - 1][k],dis在查询的过程中动态更新,这种听起来就很像正解的做法一般就是正解。

#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
const int MAXBUF=;char buf[MAXBUF],*ps=buf,*pe=buf+;
inline bool isdigit(const char& n) {return (n>=''&&n<='');}
inline void rnext(){if(++ps==pe)pe=(ps=buf)+fread(buf,sizeof(char),sizeof(buf)/sizeof(char),stdin);}
template <class T> inline bool in(T &ans){
#ifdef VSCode
ans=;T f=;register char c;
do{c=getchar();if ('-'==c)f=-;}while(!isdigit(c)&&c!=EOF);
if(c==EOF)return false;do{ans=(ans<<)+(ans<<)+c-;
c=getchar();}while(isdigit(c)&&c!=EOF);ans*=f;return true;
#endif
#ifndef VSCode
ans =;T f=;if(ps==pe)return false;do{rnext();if('-'==*ps)f=-;}
while(!isdigit(*ps)&&ps!=pe);if(ps==pe)return false;do{ans=(ans<<)+(ans<<)+*ps-;
rnext();}while(isdigit(*ps)&&ps!=pe);ans*=f;return true;
#endif
}const int MAXOUT=;
char bufout[MAXOUT], outtmp[],*pout = bufout, *pend = bufout+MAXOUT;
inline void write(){fwrite(bufout,sizeof(char),pout-bufout,stdout);pout = bufout;}
inline void out_char(char c){*(pout++)=c;if(pout==pend)write();}
inline void out_str(char *s){while(*s){*(pout++)=*(s++);if(pout==pend)write();}}
template <class T>inline void out_int(T x) {if(!x){out_char('');return;}
if(x<)x=-x,out_char('-');int len=;while(x){outtmp[len++]=x%+;x/=;}outtmp[len]=;
for(int i=,j=len-;i<j;i++,j--) swap(outtmp[i],outtmp[j]);out_str(outtmp);}
template<typename T, typename... T2>
inline int in(T& value, T2&... value2) { in(value); return in(value2...); }
#define For(i, x, y) for(int i=x;i<=y;i++)
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))
#define Sca(x) scanf("%d", &x)
#define Scl(x) scanf("%lld",&x);
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second
#define Vec Point
typedef vector<int> VI;
const double eps = 1e-;
const int maxn = ;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + ;
int N,M,tmp,K;
int a[maxn];
int dp[maxn][]; //以这个结尾
int pre[maxn];
int display[maxn];
struct Tree{
int l,r;
int MAX,lazy;
}tree[maxn * ];
void Pushdown(int t){
if(tree[t].lazy){
tree[t << ].lazy += tree[t].lazy; tree[t << ].MAX += tree[t].lazy;
tree[t << | ].lazy += tree[t].lazy; tree[t << | ].MAX += tree[t].lazy;
tree[t].lazy = ;
}
}
void Pushup(int t){
tree[t].MAX = max(tree[t << ].MAX,tree[t << | ].MAX);
}
void Build(int t,int l,int r,int flag){
tree[t].l = l; tree[t].r = r;
tree[t].lazy = ;
if(l == r){
tree[t].MAX = dp[l - ][flag];
return;
}
int m = (l + r) >> ;
Build(t << ,l,m,flag);
Build(t << | ,m + ,r,flag);
Pushup(t);
}
void update(int t,int l,int r){
if(l <= tree[t].l && tree[t].r <= r){
tree[t].MAX++;
tree[t].lazy++;
return;
}
Pushdown(t);
int m = (tree[t].l + tree[t].r) >> ;
if(r <= m) update(t << ,l,r);
else if(l > m) update(t << | ,l,r);
else{
update(t << ,l,m);
update(t << | ,m + ,r);
}
Pushup(t);
}
int query(int t,int l,int r){
if(l <= tree[t].l && tree[t].r <= r){
return tree[t].MAX;
}
Pushdown(t);
int m = (tree[t].l + tree[t].r) >> ;
if(r <= m){
return query(t << ,l,r);
}else if(l > m){
return query(t << | ,l,r);
}else{
return max(query(t << | ,m + ,r),query(t << ,l,m));
}
}
int main()
{
in(N,K);
For(i,,N) display[i] = ;
For(i,,N){
in(a[i]);
pre[i] = display[a[i]];
display[a[i]] = i;
}
For(i,,K){ //dp[i][j] = max(dp[i - 1][k] + dis[k + 1][j]) (0 <= k <= j - 1));
Build(,,N,i - );
For(j,,N){
update(,pre[j] + ,j);
dp[j][i] = query(,,j);
// cout << dp[j][i] << " ";
}
// cout << endl;
}
Pri(dp[N][K]);
#ifdef VSCode
write();
system("pause");
#endif
return ;
}

CodeForces834D DP + 线段树的更多相关文章

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

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

  2. hdu 3016 dp+线段树

    Man Down Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total S ...

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

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

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

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

  5. POJ1769 Minimizing maximizer(DP + 线段树)

    题目大概就是要,给一个由若干区间[Si,Ti]组成的序列,求最小长度的子序列,使这个子序列覆盖1到n这n个点. dp[i]表示从第0个到第i个区间且使用第i个区间,覆盖1到Ti所需的最少长度 对于Si ...

  6. [USACO2005][POJ3171]Cleaning Shifts(DP+线段树优化)

    题目:http://poj.org/problem?id=3171 题意:给你n个区间[a,b],每个区间都有一个费用c,要你用最小的费用覆盖区间[M,E] 分析:经典的区间覆盖问题,百度可以搜到这个 ...

  7. POJ 3162 Walking Race 树形DP+线段树

    给出一棵树,编号为1~n,给出数m 漂亮mm连续n天锻炼身体,每天会以节点i为起点,走到离i最远距离的节点 走了n天之后,mm想到知道自己这n天的锻炼效果 于是mm把这n天每一天走的距离记录在一起,成 ...

  8. 【uva1502/hdu4117-GRE Words】DP+线段树优化+AC自动机

    这题我的代码在hdu上AC,在uva上WA. 题意:按顺序输入n个串以及它的权值di,要求在其中选取一些串,前一个必须是后一个的子串.问d值的和最大是多少. (1≤n≤2×10^4 ,串的总长度< ...

  9. bzoj 1835 [ZJOI2010]base 基站选址(DP+线段树)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1835 [题意] 有n个村庄,每个村庄位于d[i],要求建立不多于k个基站,在第i个村庄 ...

随机推荐

  1. ULMFiT 阅读笔记

    ULMFiT 阅读笔记 概述 这篇文章从文本分类模型入手,主要提出了两点:一是预训练语言模型在大中小规模的数据集中都能提升分类效果,在小规模数据集中效果尤为显著.二是提出了多种预训练的调参方法,包括D ...

  2. 第三个spring冲刺第3天

    基本功能跟界面都完成了,今天小组开了个会,基于跟别的小组对比的效果,感觉自己组的效果没别人的好,很多方面还欠缺,所以我们会继续跟进完善.

  3. 在-for 循环里面如何利用ref 操作dom

    由于dom 元素是在渲染之后才能操作,所以如果想取到dom元素,要放到mounted()这个生命周期函数里面,并且还要用this.$nextTick(function () {})

  4. Python入门:如何使用第三方库?

    这是关于Python的第13篇文章,也是关于<编程小白的第1本Python入门书>内容的最后一篇,主要介绍下如何使用第三方库. 1. 第三方库 Python相当于一个手机,第三方库相当于手 ...

  5. Windows 下面 winrar 压缩简单记录

    1. 最高压缩比率 原始文件大小: 2.06g(3000余个文件) 压缩后文件大小:475m 压缩耗时:  210s 压缩率:22% 2. 较高压缩比率 压缩后文件大小:700 m 压缩率:32% 3 ...

  6. PLSQL 使用ODBC 数据源导入来自SQLSERVER的数据

    1. 创建ODBC数据源 方法: 打开控制命令 Win10 运行->输入 control 查看方式大图标--选择 管理工具 2. 安装了 64位的plsql 应该也选用 64位的ODBC数据源 ...

  7. 解决Ubuntu中vi命令的编辑模式下不能正常使用方向键和退格键的问题

    在Ubuntu中,进入vi命令的编辑模式,发现按方向键不能移动光标,而是会输出ABCD,以及退格键也不能正常删除字符.这是由于Ubuntu预装的是vim-tiny,而我们需要使用vim-full,解决 ...

  8. 周刷题第二期总结(Longest Substring Without Repeating Characters and Median of Two Sorted Arrays)

    这周前面刷题倒是蛮开心,后面出了很多别的事情和问题就去忙其他的,结果又只完成了最低目标. Lonest Substring Without Repeating Characters: Given a ...

  9. C从源码到运行发生了哪些事

    一个C/C++程序从源代码到可执行程序主要经历了四个阶段: ①预处理.包括展开宏.处理#include,#if,#ifdef等指令.删除注释.还有一些其他操作.相关命令:gcc -E或cpp ②编译. ...

  10. 学习笔记特别篇之orm的跨表

    models.Love.objects.filter(b__name="t1",g__nams="a1") 表示先inner join b on xx 再 in ...