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

Description

这里写图片描述

Solution

先设 fi 为选位置i时的最优答案为多少,写出一个 O(n2) 的DP,
发现这个问题实质是:在位置i之前的所有的j,保证 pj<pi ,以P为关键字所组成的单调递减的栈,求单调栈中所有位置的 fj 的最小值,

这就是一道线段树维护单调栈的板题,
pi 作为下标,i作为值,每次查询线段树中,下标为1~ pi 的所有数所组成的单调栈,在把当前的数以 pi 作为下标,i作为值,插入到线段树中,
【技巧】线段树维护区间单调栈

复杂度: O(nlog(n)2)

优化

考虑优化,我们发现,每次新加入的数的值是递增的,那么就是说,如果这个点加入到当前区间,那么在这个点之前的点都不会出现在单调栈中,
根据这个特性,在每个点维护一个单调栈表示,当前点的左区间的所有大于rmx的点所组成的单调栈,
如果新加入的点在区间右侧,那么这个单调栈肯定会被弹空,
如果在在左侧,加入这个点后的单调栈与之前的相比,原来的单调栈中,位置在这个点之前的点都不见了,变成了以这个新点打头,而原单调栈中,位置在这个新点之后的点都还在,
我们发现这个可以用一个单调栈来维护,
那么就每个区间维护一个栈,不过这个栈与实际的单调栈是刚好反过来的,

这样,每个点只会进栈 log(n) 次,出栈 log(n) 次,

复杂度: O(nlog(n))

Code

这个是 O(nlog(n)2) 的普通做法,下边的是针对这题优化过的 O(nlog(n)) 的做法,

#include <cstdio>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define max(q,w) ((q)<(w)?(w):(q))
#define SD(q) ((q)==(b[b[q].fa].r))
using namespace std;
const int N=200500,INF=1e9;
int read(int &n)
{
    char ch=' ';int q=0,w=1;
    for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
    if(ch=='-')w=-1,ch=getchar();
    for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int m,n,ans;
int f[N];
int a[N],a1[N];
bool Ansz[N];
struct qqww
{
    int lv,mi,mx;
}b[N*4];
int Pmx;
int min(int q,int w){return q<w?q:w;}
void build(int l,int r,int e)
{
    if(l!=0)b[e].lv=b[e].mi=1e9;
    if(l==r)return;
    int t=(l+r)>>1;
    build(l,t,e*2);
    build(t+1,r,e*2+1);
}
int find(int l,int r,int e,int l1,int r1,int P)
{
    if(b[e].mx<P)return 1e9;
    if(l==r)
    {
        Pmx=max(Pmx,b[e].mx);
        if(b[e].mx<P)return 1e9;
        return b[e].mi;
    }
    int t=(l+r)>>1;
    if(l1<=l&&r<=r1)
    {
        if(P>=b[e*2+1].mx)return find(l,t,e*2,l1,r1,P);
        int ans=find(t+1,r,e*2+1,l1,r1,P);
        ans=min(ans,b[e].lv);
        Pmx=max(Pmx,b[e].mx);
        return ans;
    }
    if(r1<=t)return find(l,t,e*2,l1,r1,P);
    else if(t<l1)return find(t+1,r,e*2+1,l1,r1,P);
    else 
    {
        int ans=find(t+1,r,e*2+1,t+1,r1,P);
        return min(ans,find(l,t,e*2,l1,t,Pmx));
    }
}
void change(int l,int r,int e,int l1,int l2)
{
    if(l==r)
    {
        b[e].mi=f[l2];
        b[e].mx=l2;
        return;
    }
    int t=(l+r)>>1;
    if(l1<=t)
    {
        change(l,t,e*2,l1,l2);
    }
    else 
    {
        change(t+1,r,e*2+1,l1,l2);
    }
    Pmx=0;
    b[e].lv=find(l,t,e*2,l,t,b[e*2+1].mx);
    b[e].mx=max(b[e*2].mx,b[e*2+1].mx);
    b[e].mi=min(b[e*2].mi,b[e*2+1].mi);
}
int main()
{
    freopen("knows.in","r",stdin);
    freopen("knows.out","w",stdout);
    int q,w;
    read(n);
    fo(i,1,n)read(a[i]);
    fo(i,1,n)read(a1[i]);
    q=0;
    fod(i,n,1)
    {
        if(a[i]>q)Ansz[i]=1;
        q=max(q,a[i]);
    }
    build(0,n,1);
    fo(i,1,n)
    {
        Pmx=0;
        f[i]=find(0,n,1,0,a[i],0)+a1[i];
        change(0,n,1,a[i],i);
    }
    ans=1e9;
    fo(i,1,n)if(Ansz[i])ans=min(ans,f[i]);
    printf("%d\n",ans);
    return 0;
}
#include <cstdio>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define max(q,w) ((q)<(w)?(w):(q))
#define min(q,w) ((q)>(w)?(w):(q))
#define SD(q) ((q)==(b[b[q].fa].r))
using namespace std;
const int N=200500,INF=1e9;
int read(int &n)
{
    char ch=' ';int q=0,w=1;
    for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
    if(ch=='-')w=-1,ch=getchar();
    for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int m,n,ans;
int f[N],a[N],a1[N];
bool Ansz[N];
struct qqww
{
    int lv,mi,mx,A;
}b[N*3];
int B[15*N][3],B0;
int Pmx;
void build(int l,int r,int e)
{
    if(l!=0)b[e].lv=b[e].mi=1e9;
    else b[e].A=++B0;
    if(l==r)return;
    int t=(l+r)>>1;
    build(l,t,e*2);
    build(t+1,r,e*2+1);
}
int find(int l,int r,int e,int l1,int r1,int P)
{
    if(b[e].mx<P)return 1e9;
    if(l==r)
    {
        Pmx=max(Pmx,b[e].mx);
        if(b[e].mx<P)return 1e9;
        return b[e].mi;
    }
    int t=(l+r)>>1;
    if(l1<=l&&r<=r1)
    {
        if(P>=b[e*2+1].mx)return find(l,t,e*2,l1,r1,P);
        int ans=find(t+1,r,e*2+1,l1,r1,P);
        ans=min(ans,B[b[e].A][2]);
        Pmx=max(Pmx,b[e].mx);
        return ans;
    }
    if(r1<=t)return find(l,t,e*2,l1,r1,P);
    else if(t<l1)return find(t+1,r,e*2+1,l1,r1,P);
    else 
    {
        int ans=find(t+1,r,e*2+1,t+1,r1,P);
        int ans1=find(l,t,e*2,l1,t,Pmx);
        return min(ans,ans1);
    }
}
void change(int l,int r,int e,int l1,int l2)
{
    if(l==r)
    {
        b[e].mi=f[l2];
        b[e].mx=l2;
        return;
    }
    int t=(l+r)>>1;
    if(l1<=t)
    {
        change(l,t,e*2,l1,l2);
        for(;b[e].A&&B[b[e].A][1]<l1;b[e].A=B[b[e].A][0]);
        B[++B0][0]=b[e].A;B[B0][2]=min(f[l2],B[b[e].A][2]);
        b[e].A=B0;B[B0][1]=l1;
    }
    else 
    {
        change(t+1,r,e*2+1,l1,l2);
        b[e].A=0;
    }
    b[e].mx=max(b[e*2].mx,b[e*2+1].mx);
    b[e].mi=min(b[e*2].mi,b[e*2+1].mi);
}
int main()
{
    int q,w;
    read(n);
    fo(i,1,n)read(a[i]);
    fo(i,1,n)read(a1[i]);
    q=0;
    fod(i,n,1)
    {
        if(a[i]>q)Ansz[i]=1;
        q=max(q,a[i]);
    }
    build(0,n,1);
    B[0][2]=1e9;
    fo(i,1,n)
    {
        Pmx=0;
        f[i]=find(0,n,1,0,a[i],0)+a1[i];
        change(0,n,1,a[i],i);
    }
    ans=1e9;
    fo(i,1,n)if(Ansz[i])ans=min(ans,f[i]);
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值