JZOJ 5402. 【NOIP2017提高A组模拟10.8】God Knows

Description

JZOJ 5402. 【NOIP2017提高A组模拟10.8】God Knows_第1张图片

Input

Input

Output

Output

Sample Input

5
3 1 4 5 2
3 4 3 4 1

Sample Output

5

Data Constraint

JZOJ 5402. 【NOIP2017提高A组模拟10.8】God Knows_第2张图片

Solution

  • 很容易想到 O(N2) DP。

  • f[i] 表示当最后一个选 i 为最优答案时的最小代价。

  • 枚举符合条件的 j 转移即可。

  • 考虑优化 DP 。

  • (p[i],i) 放到坐标系里,如下图:

JZOJ 5402. 【NOIP2017提高A组模拟10.8】God Knows_第3张图片

  • i 从小到大的顺序加入,那么对于一个 i 能够转移的 j

  • 显然就是这个点左边部分单调下降的一行点。

  • 即以这个点 j 为左下角、点 i 为右上角的矩形中间没有其他点。

  • 如上图,对于黄色的点,能转移的点就是绿色和蓝色的点。

  • 那么这就是经典的线段树维护单调栈的问题。

  • 记录一个 mn=find(l,mid,rmx) ,那么区间的答案就是

    min(mn,find(mid+1,r,rmx))

  • 这样就能保证时间复杂度为 O(N log2N)

Code

#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;
}

你可能感兴趣的:(动态规划,线段树,堆-栈-队列,单调性)