CF1527E 题解

CF1527E

思路

dp 题。

\(dp_{i,j}\) 表示前 \(i\) 个数分为 \(j\) 段的最小代价。

式子:

\(dp_{i,j}=\min_{k=1}^{i-1} (dp_{k,j-1}+w(k+1,i))\)

压一维:

\(dp_{i}=\min_{k=1}^{i} (dp_{k-1}+w(k,i))\)

其中,\(w(i,j)\) 表示题目中的 \(cost(sub(i,j))\)

问题在于快速求 \(w(i,j)\)

\(b_k=dp_{k-1}+w(k,i)\),则 \(dp_i=\min_{k=1}^{i} dp_k\)。每次转移需要求出数组 \(b\) 的最小值。每个 \(a_i\) 的对 \(b\) 贡献是 \(b_1\)\(b_{lst_{a_j}}\) 加上 \(i-lst_{a_j}\),其中 \(lst_i\) 是数值 \(i\) 上次在 \(a\) 中出现的位置。

所以要维护 \(b\),区间加和最小值,使用线段树。

用上一轮的 \(dp\) 值建树,从 \(1\)\(n\),每读进一个 \(a_i\)\(upd\) 更新线段树,\(que\) 询问最小值即为 \(dp_i\)

\(O(kn\log n)\)

code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=35010;

int n,k,a[maxn];
int tree[maxn<<2],tag[maxn<<2],dp[maxn];
int lst[maxn],pre[maxn];
void build(int nd,int l,int r){
	if(l==r){
		tree[nd]=dp[l-1];
		return ;
	}
	int mid=l+r>>1,ls=nd<<1,rs=nd<<1|1;
	build(ls,l,mid);build(rs,mid+1,r);
	tree[nd]=min(tree[ls],tree[rs]);
}
void push(int nd,int l,int r){
	int mid=l+r>>1,ls=nd<<1,rs=nd<<1|1;
	tree[ls]+=tag[nd];
	tag[ls]+=tag[nd];
	tree[rs]+=tag[nd];
	tag[rs]+=tag[nd];
	tag[nd]=0;
}
void updata(int nd,int l,int r,int x,int y,int w){
	if(l>=x&&r<=y){
		tree[nd]+=w;
		tag[nd]+=w;
		return ;
	}
	if(tag[nd])push(nd,l,r);
	int mid=l+r>>1,ls=nd<<1,rs=nd<<1|1;
	if(x<=mid)updata(ls,l,mid,x,y,w);
	if(y>mid)updata(rs,mid+1,r,x,y,w);
	tree[nd]=min(tree[ls],tree[rs]);
}
int query(int nd,int l,int r,int x,int y){
	if(l>=x&&r<=y){
		return tree[nd];
	}
	if(tag[nd])push(nd,l,r);
	int mid=l+r>>1,ls=nd<<1,rs=nd<<1|1;
	if(y<=mid)return query(ls,l,mid,x,y);
	if(x>mid)return query(rs,mid+1,r,x,y);
	return min(query(ls,l,mid,x,y),query(rs,mid+1,r,x,y));
}
void sovle(){
	cin>>n>>k;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++){
		lst[i]=pre[a[i]];
		pre[a[i]]=i;
	}
	memset(dp,0x3f,sizeof(dp));dp[0]=0;
	for(int i=1;i<=k;i++){
		memset(tag,0,sizeof(tag));
		build(1,1,n);
		for(int j=1;j<=n;j++){
			if(lst[j])updata(1,1,n,1,lst[j],j-lst[j]);
			dp[j]=query(1,1,n,1,j);
		}
	}
	cout<<dp[n]<<"\n";
}

int T;
signed main(){
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);

//	cin.tie(0);cout.tie(0);
//	ios::sync_with_stdio(false);
	T=1;
	while(T--)sovle();
}

套路

CF833B The Bakery

式子同样形如:\(dp_{i}=\max_{k=1}^{i} (dp_{k-1}+w(k,i))\)

考虑每个 \(a_i\)\(dp\) 的贡献。

int n,k,a[maxn];
int tree[maxn<<2],dp[maxn],tag[maxn<<2];
int pre[maxn],lst[maxn];
void sovle(){
	cin>>n>>k;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++){
		lst[i]=pre[a[i]]+1;
		pre[a[i]]=i;
	}
	for(int i=1;i<=k;i++){
		memset(tree,0,sizeof(tree));
		memset(tag,0,sizeof(tag));
		build(1,1,n);
		for(int j=1;j<=n;j++){
			updata(1,1,n,lst[j],j);
			dp[j]=query(1,1,n,1,j);
		}
	}
	cout<<dp[n]<<"\n";
}
posted @ 2024-05-10 19:58  yhddd  阅读(5)  评论(0编辑  收藏  举报