AtCoder Educational DP Contest Q - Flowers 题解
发表于|更新于
|阅读量:
题意
有一排花,共 n 个,第 i 个的高度是 hi ,权值是 ai ,保证高度互不相同。现在拿走一些花,使剩下的花高度单调递增,问剩下的花权值之和最大是多少。
数据范围与约定:
1≤n≤2×105,1≤hi≤n,1≤ai≤109,h1,h2,⋅⋅⋅,hN的值均不相等。
前置知识
前置知识:线段树、动态规划
思路
看到这题我们能想到一个很显然的dp做法:设f[i]为取前i朵花中的一些花,且第i朵最高时能取得的最大权值,则f[i]=max{f[j],j<i,h[j]<h[i]}+a[i],在每次取到i时再遍历一次f[1]至f[i−1]即可。可是这样做的复杂度在是O(n2),每次取一个i都需要从头遍历一次,显然在这题的数据范围下会tle。
于是我们考虑如何优化取得f[j]max(j<i)的过程。对于求区间最值问题,往往用线段树对区间最值进行维护是一个十分方便的途径:
在这道题目中,对于第i朵花,更新答案f[i]时查询高度在区间[1,h[i]−1]内f数组的最大值,更新完f[i]之后再将f[i]插入到线段树中,每次查询和插入的时间复杂度是O(logn),需要进行n次操作。
于是我们可以在O(nlogn)的时间内解决本题。
需要注意的细节是,在求解完f数组的全部值之后,f[n]并不一定是要求的答案,因为第n朵花可能并不够高,所以我们还需要遍历一遍f数组找出最大值,才能求出答案。
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| int n; struct flower{ ll h; ll a; } fl[200005]; ll tree[800005],ans[200005]; inline void pushup(int p){tree[p] = max(tree[p<<1],tree[(p<<1)+1]);} void modify(int d,int l,int r,int p,ll val){ if(l==r && r==d){tree[p] = max(tree[p],val);} else{ int mid = (l+r)>>1; if(mid>=d){modify(d,l,mid,p<<1,val);} else{modify(d,mid+1,r,(p<<1)+1,val);} pushup(p); } } ll query(int l,int r,int nl,int nr,int p){ if(nl>r || nr<l) return 0; else{ if(nl>=l && nr<=r) return tree[p]; else{ int mid = (nl+nr)>>1; return max(query(l,r,nl,mid,p<<1),query(l,r,mid+1,nr,(p<<1)+1)); } } } void solve(){ memset(tree,0,sizeof(tree)); read(n); for(int i=1;i<=n;i++) read(fl[i].h); for(int i=1;i<=n;i++) read(fl[i].a); for(int i=1;i<=n;i++){ ans[i] = query(1,fl[i].h-1,1,n,1)+fl[i].a; modify(fl[i].h,1,n,1,ans[i]); } ll maxans = 0; for(int i=1;i<=n;i++) maxans = max(maxans,ans[i]); write(maxans); } int main(){ solve(); return 0; }
|
其他
恭喜拿不拿的海百合海底谭成功神话!