AT4538 Flowers

题意

一个长度为 nn 的序列,每个元素有两个权值 hi,aih_i,a_i,从这个序列中选出一个子序列,并满足以下要求:

  • hih_i 单调递增。

  • 在满足上一条的情况下,这个子序列的 aia_i 之和最大。

分析

dpidp_i 表示以 ii 结尾的符合要求的子序列。

根据题意,则:

dpi=maxj=1i1{dpj}(hj<hi)+aidp_i=\max_{j=1}^{i-1}\{dp_j\}(h_j<h_i)+a_i

这样做是 O(n2)O(n^2) 的,显然过不了,不过题目中 hih_i 比较小,所以我们从 hih_i 入手。

其实,我们要找的 dpjdp_j 就是 ii 之前所有高度在 1hi11\sim h_i-1 之间的 dpjdp_j 中最大的一个。

这个显然可以用数据结构维护,我们需要一个支持单点修改,区间查询最大值的数据结构,树形结构比较麻烦,我们可以用分块来做。

本题只要维护一个前缀最大值,所以查询的区间中只有右边是散块,这样就很好写了,前面部分统计整块最大值,后面部分暴力统计。

单点修改更是简单,两行解决,把 hih_i 所在的点和块更新即可。

代码

时间复杂度 O(nn)O(n\sqrt n)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
long long read(){
	long long x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
const int N=2e5+10,M=600;
int n;
ll dp[N],b[N],k[N],h[N],a[N],len,mx;
void change(int x,ll y){
	b[x]=max(b[x],y);
	k[x/len]=max(k[x/len],y);
}
ll ask(int x){
	ll mx=0;
	for(int i=0;i<x/len;i++)
		mx=max(mx,k[i]);
	for(int i=x/len*len;i<=x;i++)
		mx=max(mx,b[i]);
	return mx;
}
int main(){
	n=read();
	len=sqrt(n);
	for(int i=1;i<=n;i++)
		h[i]=read();
	for(int i=1;i<=n;i++)
		a[i]=read();
	for(int i=1;i<=n;i++){
		dp[i]=ask(h[i]-1)+a[i];
		change(h[i],dp[i]);
		mx=max(mx,dp[i]);
	}
	cout<<mx;
	return 0;
}
posted @ 2021-08-29 15:23  luckydrawbox  阅读(4)  评论(0)    收藏  举报  来源