5
3 1 4 5 2
3 4 3 4 1
5
很容易想到 O(N2) DP。
设 f[i] 表示当最后一个选 i 为最优答案时的最小代价。
枚举符合条件的 j 转移即可。
考虑优化 DP 。
把 (p[i],i) 放到坐标系里,如下图:
按 i 从小到大的顺序加入,那么对于一个 i 能够转移的 j ,
显然就是这个点左边部分单调下降的一行点。
即以这个点 j 为左下角、点 i 为右上角的矩形中间没有其他点。
如上图,对于黄色的点,能转移的点就是绿色和蓝色的点。
那么这就是经典的线段树维护单调栈的问题。
记录一个 mn=find(l,mid,rmx) ,那么区间的答案就是
这样就能保证时间复杂度为 O(N log2N) 。
#include
#include
using namespace std;
const int N=2e5+5,inf=1e9;
struct data
{
int mx,mn;
}g[N<<2];
int rmx,qx,qy,ans=inf;
int p[N],c[N],f[N];
inline int read()
{
int X=0,w=0; char ch=0;
while(!isdigit(ch)) w|=ch=='-',ch=getchar();
while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
return w?-X:X;
}
inline int max(int x,int y)
{
return x>y?x:y;
}
inline int min(int x,int y)
{
return xint calc(int v,int l,int r,int val)
{
if(g[v].mxreturn inf;
if(l==r) return g[v].mx>val?f[g[v].mx]:inf;
int mid=l+r>>1;
if(g[v<<1|1].mxreturn calc(v<<1,l,mid,val);
return min(g[v].mn,calc(v<<1|1,mid+1,r,val));
}
int find(int v,int l,int r,int x,int y)
{
if(x<=l && r<=y)
{
int num=calc(v,l,r,rmx);
rmx=max(rmx,g[v].mx);
return num;
}
int mid=l+r>>1;
if(y<=mid) return find(v<<1,l,mid,x,y);
if(x>mid) return find(v<<1|1,mid+1,r,x,y);
int num=find(v<<1|1,mid+1,r,mid+1,y);
return min(num,find(v<<1,l,mid,x,mid));
}
void change(int v,int l,int r)
{
if(l==r)
{
g[v].mx=qy;
return;
}
int mid=l+r>>1;
if(qx<=mid) change(v<<1,l,mid); else change(v<<1|1,mid+1,r);
g[v].mn=calc(v<<1,l,mid,g[v<<1|1].mx);
g[v].mx=max(g[v<<1].mx,g[v<<1|1].mx);
}
int main()
{
int 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<<2;i++) g[i].mn=inf;
for(int i=1;i<=n;i++)
{
rmx=0;
int num=find(1,1,n,1,p[i]);
f[i]=(num0)+c[i];
qx=p[i],qy=i;
change(1,1,n);
}
for(int i=n,num=0;i;i--)
if(p[i]>num) num=p[i],ans=min(ans,f[i]);
printf("%d",ans);
return 0;
}