题目链接

\(Description\)

有n个数,将其分为k段,每段的值为这一段的总共数字种类,问最大总值是多少

\(Solution\)

DP,用\(f[i][j]\)表示当前在i 分成了j份(第j份包括i)

那枚举前边的断点k,即

\(f[i][j]=max{f[k][j-1]+val(k+1,1)}\)

\(val(a,b)\)表示\([a,b]\)这段区间的价值(数字种数)

\(O(n^2*k)\)

第二维可以滚动数组优化掉,只在最外层枚举即可

优化求\(val()\)的过程

val是与数的种类数有关,所以对于a[i],在计算\([1,las[a[i]]]\)的val[]时,a[i]不会做出贡献;

而用\([las[a[i]]+1,i]\)计算val时,a[i]会有1的贡献

即用\(f[k](k∈[1,\ las[a[i]]-1])\)时,不会有a[i]的贡献;

而用\(f[k](k∈[las[a[i]],\ i-1])\)更新时,a[i]会对其产生贡献(k这个点是不在后一个区间里的)

在这段区间+1,然后查询最优值,更新f[i]

区间加、区间查询最优值 -> 线段树

/*
592ms 8400KB
注意1.可以从f[0]更新
2.查询位置与第一层循环的关系
3.开四倍空间... 基本线段树都不会写了...
*/
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
//#define gc() getchar()
#define gc() (SS==IN &&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
const int N=35005,MAXIN=5e6; int n,k,A[N],f[N],las[N],tmp[N];
char IN[MAXIN],*SS=IN,*TT=IN;
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
struct Seg_Tree
{
int maxv[N<<2],tag[N<<2];
inline void PushUp(int rt)
{
maxv[rt]=std::max(maxv[rt<<1],maxv[rt<<1|1]);
}
inline void PushDown(int rt)
{
maxv[rt<<1]+=tag[rt], maxv[rt<<1|1]+=tag[rt];
tag[rt<<1]+=tag[rt], tag[rt<<1|1]+=tag[rt];
tag[rt]=0;
}
void Build(int l,int r,int rt)
{
tag[rt]=0;
if(l==r) {maxv[rt]=f[l]; return;}
int m=l+r>>1;
Build(l,m,rt<<1), Build(m+1,r,rt<<1|1);
PushUp(rt);
}
void Modify(int l,int r,int rt,int L,int R)
{
if(L<=l && r<=R)
{
++maxv[rt], ++tag[rt];
return;
}
if(tag[rt]) PushDown(rt);
int m=l+r>>1;
if(L<=m) Modify(l,m,rt<<1,L,R);
if(m<R) Modify(m+1,r,rt<<1|1,L,R);
PushUp(rt);
}
int Query(int l,int r,int rt,int L,int R)
{
if(L<=l && r<=R) return maxv[rt];
if(tag[rt]) PushDown(rt);
int m=l+r>>1;
if(L<=m)
if(m<R) return std::max(Query(l,m,rt<<1,L,R),Query(m+1,r,rt<<1|1,L,R));
else return Query(l,m,rt<<1,L,R);
else return Query(m+1,r,rt<<1|1,L,R);
}
}t; int main()
{
#ifndef ONLINE_JUDGE
freopen("D.in","r",stdin);
#endif n=read(),k=read();
for(int i=1;i<=n;++i)
A[i]=read(), las[i]=tmp[A[i]], tmp[A[i]]=i;
// for(int i=1;i<=n;++i) printf("%d %d\n",A[i],las[i]);
for(int i=1;i<=k;++i)
{
t.Build(0,n,1);
for(int j=i;j<=n;++j)
t.Modify(0,n,1,las[j],j-1),f[j]=t.Query(0,n,1,i-1,j-1);
}
printf("%d",f[n]); return 0;
}

堆式存储:(多维护了ls,rs而只少了两倍空间,所以空间优化不大)(写这个纯粹闲的)

/*
624ms 9400KB 和另一个差不了多少 注意1.可以从f[0]更新
2.查询位置与第一层循环的关系
*/
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
//#define gc() getchar()
#define gc() (SS==IN &&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
#define lson node[rt].ls
#define rson node[rt].rs
const int N=35005,MAXIN=5e6; int n,k,A[N],f[N],las[N],tmp[N];
char IN[MAXIN],*SS=IN,*TT=IN;
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
struct Seg_Tree
{
int tot;
struct Node
{
int ls,rs,maxv,tag;
}node[N<<1];
inline void PushUp(int rt)
{
node[rt].maxv=std::max(node[lson].maxv,node[rson].maxv);
}
inline void PushDown(int rt)
{
node[lson].maxv+=node[rt].tag, node[rson].maxv+=node[rt].tag;
node[lson].tag+=node[rt].tag, node[rson].tag+=node[rt].tag;
node[rt].tag=0;
}
void Build(int l,int r)
{
int p=tot++;
node[p].tag=0;
if(l==r) { node[p].ls=node[p].rs=-1,node[p].maxv=f[l]; return;}
int m=l+r>>1;
node[p].ls=tot, Build(l,m);
node[p].rs=tot, Build(m+1,r);
PushUp(p);
}
void Modify(int l,int r,int rt,int L,int R)
{
if(L<=l && r<=R)
{
++node[rt].maxv, ++node[rt].tag;
return;
}
if(node[rt].tag) PushDown(rt);
int m=l+r>>1;
if(L<=m) Modify(l,m,lson,L,R);
if(m<R) Modify(m+1,r,rson,L,R);
PushUp(rt);
}
int Query(int l,int r,int rt,int L,int R)
{
if(L<=l && r<=R) return node[rt].maxv;
if(node[rt].tag) PushDown(rt);
int m=l+r>>1;
if(L<=m)
if(m<R) return std::max(Query(l,m,lson,L,R),Query(m+1,r,rson,L,R));
else return Query(l,m,lson,L,R);
else return Query(m+1,r,rson,L,R);
}
}t; int main()
{
#ifndef ONLINE_JUDGE
freopen("D.in","r",stdin);
#endif n=read(),k=read();
for(int i=1;i<=n;++i)
A[i]=read(), las[i]=tmp[A[i]], tmp[A[i]]=i;
// for(int i=1;i<=n;++i) printf("%d %d\n",A[i],las[i]);
for(int i=1;i<=k;++i)
{
t.tot=0, t.Build(0,n);
for(int j=i;j<=n;++j)
t.Modify(0,n,0,las[j],j-1),f[j]=t.Query(0,n,0,i-1,j-1);
}
printf("%d",f[n]); return 0;
}

Codeforces.833B.The Bakery(线段树 DP)的更多相关文章

  1. CodeForces 834D The Bakery(线段树优化DP)

    Some time ago Slastyona the Sweetmaid decided to open her own bakery! She bought required ingredient ...

  2. codeforces#426(div1) B - The Bakery (线段树 + dp)

    B. The Bakery   Some time ago Slastyona the Sweetmaid decided to open her own bakery! She bought req ...

  3. Codeforces.264E.Roadside Trees(线段树 DP LIS)

    题目链接 \(Description\) \(Solution\) 还是看代码好理解吧. 为了方便,我们将x坐标左右反转,再将所有高度取反,这样依然是维护从左到右的LIS,但是每次是在右边删除元素. ...

  4. CF833B The Bakery (线段树+DP)

    题目大意:给你一个序列(n<=35000),最多分不大于m块(m<=50),求每个块内不同元素的数量之和的最大值 考试的时候第一眼建图,没建出来,第二眼贪心 ,被自己hack掉了,又随手写 ...

  5. [Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路)

    [Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路) 题面 有n个空心物品,每个物品有外部体积\(out_i\)和内部体积\(in_i\),如果\(in_i& ...

  6. Tsinsen A1219. 采矿(陈许旻) (树链剖分,线段树 + DP)

    [题目链接] http://www.tsinsen.com/A1219 [题意] 给定一棵树,a[u][i]代表u结点分配i人的收益,可以随时改变a[u],查询(u,v)代表在u子树的所有节点,在u- ...

  7. HDU 3016 Man Down (线段树+dp)

    HDU 3016 Man Down (线段树+dp) Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Ja ...

  8. Buses and People CodeForces 160E 三维偏序+线段树

    Buses and People CodeForces 160E 三维偏序+线段树 题意 给定 N 个三元组 (a,b,c),现有 M 个询问,每个询问给定一个三元组 (a',b',c'),求满足 a ...

  9. CodeForces 877E DFS序+线段树

    CodeForces 877E DFS序+线段树 题意 就是树上有n个点,然后每个点都有一盏灯,给出初始的状态,1表示亮,0表示不亮,然后有两种操作,第一种是get x,表示你需要输出x的子树和x本身 ...

随机推荐

  1. 【网络编程2】网络编程基础-发送ICMP包(Ping程序)

    IP协议 网络地址和主机协议 位于网络层的协议,主要目的是使得网络能够互相通信,该协议使用逻辑地址跨网络通信,目前有两个版本IPV4,IPV6. 在IPV4协议中IP地址是一个32位的数备,采用点分四 ...

  2. maven 跳过test

    -DskipTests,不执行测试用例,但编译测试用例类生成相应的class文件至target/test-classes下. -Dmaven.test.skip=true,不执行测试用例,也不编译测试 ...

  3. Android设计模式-观察者模式

    原文地址 http://blog.csdn.net/qq_25806863/article/details/69218968 观察者模式是一种使用频率非常高的设计模式,最常用的地方就是订阅-发布系统. ...

  4. python----线程进程协程

    python线程: import threading import time def show(arg): time.sleep() print('thread' + str(arg)) ): t = ...

  5. JMeter 聚合报告 90%响应时间

    90%的响应时间理解 官方解释:90% Line - 90% of the samples took no more than this time. The remaining samples at ...

  6. Oracle中数值的计算

    运算符 含义 · +(加)   加法 · ||(加) 字符串相加 · -(减)   减法 · *(乘)   乘法 · /(除)   除法 · mod(模)返回一个除法的整数余数  例如,12 % 5 ...

  7. 【C++ Primer | 15】C++类内存分布

    C++类内存分布 书上类继承相关章节到这里就结束了,这里不妨说下C++内存分布结构,我们来看看编译器是怎么处理类成员内存分布的,特别是在继承.虚函数存在的情况下. 下面可以定义一个类,像下面这样: c ...

  8. 查看Linux端口的占用及连接情况

    [root@cloudplatform ~]# netstat -nap | grep 22066 | grep 127.0.0.1 -vtcp        0      0 :::22066    ...

  9. android app 流量统计

    https://blog.csdn.net/yzy9508/article/details/48300265 | android 数据流量统计 - CSDN博客https://blog.csdn.ne ...

  10. 在django中,redirect如何传递message。

    众所周知,在django中,默认的message,只能在同一个request中传递. 但如果在请求过程中,使用了redirect跳转,那么,这个一次性的message就会失败, 无法在前端给用户提示. ...