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. Daily Scrumming* 2015.12.21(Day 13)

    一.团队scrum meeting照片 大部分成员编译请假,故今天没有开scrum meeting 二.成员工作总结 姓名 任务ID 迁入记录 江昊 无 无 任务说明: 今日准备编译测验,请假 遇到问 ...

  2. 《Linux内核设计与实现》第四章学习笔记

    <Linux内核设计与实现>第四章学习笔记           ——进程调度 姓名:王玮怡  学号:20135116 一.多任务 1.多任务操作系统的含义 多任务操作系统就是能同时并发地交 ...

  3. HTTP基础与Android之(安卓与服务器通信)——使用HttpClient和HttpURLConnection

    查看原文:http://blog.csdn.net/sinat_29912455/article/details/51122286 1客户端连接服务器实现内部的原理 GET方式和POST方式的差别 H ...

  4. 注解Annotation

    @java.lang.annotation.Target(value={java.lang.annotation.ElementType.TYPE}) @java.lang.annotation.Re ...

  5. Linux下安装oracle的过程

    1. Linux 安装 主要不要将home分区设置的特别大 2. 安装必须的一些包. yum install -y \ binutils.x86_64 \ elfutils-libelf-devel. ...

  6. [日常工作]GS使用安装盘修改密码后的处理

    1. GS服务器端有时候需要修改 9999 的用户密码.但是修改完密码之后有几个注意事项: 一般有两个可以修改注册的地方: 使用setup里面的修改用户密码 修改完密码之后要手工注册一下数据库实例 也 ...

  7. Git 换行符检查 CRLF 与 LF

    遇到的问题 在 git 提交或是签出时,提示如下问题: [git] warning: LF will be replaced by CRLF | fatal: CRLF would be replac ...

  8. IntersectionObserver简介

    写在前面 在移动端,有个很重要的概念,叫做懒加载,适用于一些图片资源特别多,ajax数据特别多的页面中,经常会有动态加载数据的场景中,这个时候,我们通常是使用监听scroll或者使用setInterv ...

  9. python之参数解包

    # 参数解包:将整个list当做参数传给函数 list = [1, 2, 4] def add_fn(a, b, c): return a + b + c sum = add_fn(*list) pr ...

  10. Square Root

    Square RootWhen the square root functional configuration is selected, a simplified CORDIC algorithm ...