题目:The Bakery
之前没有好好听@kevin_yu讲题,做了一晚上……
这篇题解主要是对kevin_yu的补充以及代码注释的扩充。
思路:
part 1.dp
一个很容易想到的
O
(
n
2
k
)
O(n^2k)
O(n2k)算法——
令
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示前i的位置划分j次的价值。
转移方程:
f
[
i
]
[
j
]
=
m
a
x
(
f
[
k
]
[
j
−
1
]
+
c
n
t
[
k
]
[
i
]
)
f[i][j]=max \ (f[k][j-1]+cnt[k][i] )
f[i][j]=max (f[k][j−1]+cnt[k][i])
其中,k是一个中间值,代表划分枚举的位置。
然后,我们知道这样做一定会超时,所以我们需要优化。
part 2.线段树
我们选择优化转移,也就是说省去中间值k的枚举。
再看我们的转移方程,可以发现,我们取得是
f
[
k
]
[
j
−
1
]
+
c
n
t
[
k
]
[
i
]
f[k][j-1]+cnt[k][i]
f[k][j−1]+cnt[k][i]这一段的最大值。
对于这种类型的取max的dp的优化,可以选择 单调队列优化 / nlogn数据结构优化。
如果使用单调队列,我们可以处理出
m
a
x
f
[
k
]
[
j
−
1
]
\ max\ {f[k][j-1]}
max f[k][j−1],但是
c
n
t
[
k
]
[
i
]
cnt[k][i]
cnt[k][i]这种数据却无法操作。
所以考虑数据结构,也就是动态区间最值的最简单工具——线段树。
令pre[i]表示上一次出现与i同颜色的蛋糕的位置。
假设我们在pre[i]和i之间取分割点k,那么末点在区间
[
k
,
i
]
[k,i]
[k,i]间都是可以拥有col[i]的,而
[
p
r
e
[
i
]
,
k
)
[\ pre[i] , k)
[ pre[i],k)这一段一定是没有col[i]的。
这样看,我们每次在线段树上更新
[
p
r
e
[
i
]
,
i
]
[pre[i],i]
[pre[i],i],查询
[
1
,
i
]
[1,i]
[1,i]就可以处理cnt的问题了。
而f[k][j-1]只需要在建树时加上就好。
具体实现见代码及注释——
代码:
#include<bits/stdc++.h>
using namespace std;
#define maxn 35000
#define maxm 50
#define read(x) scanf("%d",&x)
#define lson (o*2)
#define rson (o*2+1)
#define mid (L+(R-L)/2)
int n,m;
int f[maxn+5][maxm+5]; //f[i][j]:前i的位置划分j次的价值
int pre[maxn+5]; //上一次出现与i同颜色的蛋糕的位置
map<int,int> mp; //用来更新pre
//线段树部分
int a[maxn*10+5]; //线段树
int lzy[maxn*10+5]; //lazy tag
void push_up(int o) { //通过子节点更新父节点的值
a[o]=max(a[lson],a[rson]);
}
void make_tree(int o,int L,int R,int row) { //建树
a[o]=lzy[o]=0;
if(L==R) {
a[o]=f[L-1][row-1];
return ;
}
make_tree(lson,L,mid,row),make_tree(rson,mid+1,R,row);
push_up(o);
}
void push_down(int o) { //下传lazy tag
a[lson]+=lzy[o],a[rson]+=lzy[o];
lzy[lson]+=lzy[o],lzy[rson]+=lzy[o];
lzy[o]=0;
}
int P,Q; //修改及查询的区间
void update(int o,int L,int R) { //更新
if(L>Q||R<P) return ;
if(L>=P&&R<=Q) {
lzy[o]++;
a[o]++;
return ;
}
push_down(o);
update(lson,L,mid),update(rson,mid+1,R);
push_up(o);
}
int query(int o,int L,int R) { //查询
if(L>Q||R<P) return 0;
if(L>=P&&R<=Q) {
return a[o];
}
push_down(o);
return max(query(lson,L,mid),query(rson,mid+1,R));
}
int main() {
read(n),read(m);
for(int i=1;i<=n;i++) {
int x;
read(x);
if(mp.count(x)) pre[i]=mp[x]+1;
else pre[i]=1;
mp[x]=i;
}
for(int j=1;j<=m;j++) {
make_tree(1,1,n,j);
for(int i=1;i<=n;i++) {
P=pre[i],Q=i;
update(1,1,n);
P=1,Q=i;
f[i][j]=query(1,1,n);
}
}
printf("%d",f[n][m]);
return 0;
}