God Knows 线段树维护单调栈

来源:互联网 发布:abaqus软件 编辑:程序博客网 时间:2024/06/06 03:10

题意

这里写图片描述
这里写图片描述

分析

不难分析到这题实际就是要求一个极长的上升子序列,使得其权值和最小。
首先考虑最暴力的dp:设f[i]表示以i结尾且不考虑后面元素时的极长上升子序列最小值。转移的话,就可以选择所有满足可以转移且转移后仍为极大的j来转移即可。复杂度是O(n^2)

接下来用到的姿势跟bzoj 2957比较类似,都是用线段树来维护一个单调栈的贡献。

我们把元素按照p[i]来排序,那么当我们要转移f[i]时,可以用来转移的j一定是这些数构成的单调栈上的数。也就是所以p[i]为下标,i为单调栈元素。那么我们要维护的实际上就是一个区间内单调栈的最小值。
那么我们在dp的时候,先在线段树中查询[1,p[i]]的值,把f[i]求出来,然后把线段树的p[i]位置加入一个i即可。

用线段树维护单调栈的具体操作就是每个节点记录只考虑当前区间所构成的单调栈的贡献和栈顶元素。在合并儿子的时候,右儿子的单调栈肯定是不变的,那么我们就可以递归计算左儿子的贡献。
而左儿子油杯分成了两部分,设为l和r。
设右儿子的栈顶元素为mx,若mx[r]>mx那么l的单调栈肯定是不变的,然后递归计算r的贡献。若mx[r]

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>using namespace std;const int N=200005;const int inf=2000000000;int n,p[N],c[N],f[N];struct tree{int l,r,mx,mn;}t[N*5];int read(){    int x=0,f=1;char ch=getchar();    while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}int calc(int d,int l,int r,int x){    if (l==r) return t[d].mx>x?t[d].mn:inf;    int mid=(l+r)/2;    if (t[d*2+1].mx>x) return min(t[d].l,calc(d*2+1,mid+1,r,x));    else return calc(d*2,l,mid,x);}void ins(int d,int l,int r,int x,int y,int z){    if (l==r)    {        t[d].mn=z;t[d].mx=y;        return;    }    int mid=(l+r)/2;    if (x<=mid) ins(d*2,l,mid,x,y,z);    else ins(d*2+1,mid+1,r,x,y,z);    t[d].r=t[d*2+1].mn;    t[d].l=calc(d*2,l,mid,t[d*2+1].mx);    t[d].mx=max(t[d*2].mx,t[d*2+1].mx);    t[d].mn=min(t[d].l,t[d].r);}pair<int,int> query(int d,int l,int r,int x,int y){    if (x>y) return make_pair(inf,0);    if (l==x&&r==y) return make_pair(t[d].mn,t[d].mx);    int mid=(l+r)/2;    if (y<=mid) return query(d*2,l,mid,x,y);    else    {        pair<int,int> u=query(d*2+1,mid+1,r,mid+1,y);        return make_pair(min(u.first,calc(d*2,l,mid,u.second)),max(u.second,t[d*2].mx));    }}int main(){    freopen("knows.in","r",stdin);freopen("knows.out","w",stdout);    n=read();    for (int i=1;i<=n;i++) p[i]=read();    for (int i=1;i<=n;i++) c[i]=read();    for (int i=1;i<=n*4;i++) t[i].mn=t[i].l=t[i].r=inf;    int mn=inf;    for (int i=1;i<=n;i++)    {        f[i]=c[i];mn=min(mn,p[i]);        if (p[i]>mn) f[i]+=query(1,1,n,1,p[i]).first;        ins(1,1,n,p[i],i,f[i]);    }    int mx=0,ans=inf;    for (int i=n;i>=1;i--) if (p[i]>mx) mx=p[i],ans=min(ans,f[i]);    printf("%d",ans);    return 0;}