bzoj2221: [Jsoi2009]面试的考验

之前GDOI的时候了解过,随机数据的用途就是在其中找一段单调上升/下降的子序列期望长度为logn

然而这道题还是不懂怎么用的说。。

首先考虑对于i<j<k,若ci>cj>ck则对于k来说i是无用的。

也就是说对于当前点k,我们需要找到一个后往前单调下降的子序列并且这些数都比c[k]大

对于比c[k]小的我们同理找一个单调上升的子序列

 

具体做法就是枚举右端点,对于前面找一个后往前单调下降并且这些数都比c[k]大的的子序列,然后一一更新这些点的最小值。离散值作为线段树的下标,利用线段树找区间最大的原下标就可以logn找到下一个位置。对于答案按右端点排序,求区间内每个点的最小值即可。

维护复杂度是nlog^2n的,询问是Qlogn

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;

int c[110000],lslen,ls[110000],g[110000];
struct query{int l,r,id;}q[110000];int as[110000];
bool cmp(query q1,query q2){return q1.r<q2.r;}

//----------------------------------------------------------------------

struct trnode
{
    int l,r,lc,rc;
    int c;
    int v;
}tr[210000];int trlen;
void bt(int l,int r) 
{
    int now=++trlen;
    tr[now].l=l;tr[now].r=r;
    tr[now].lc=tr[now].rc=-1;
    tr[now].c=(1<<30);
    tr[now].v=0;
    
    if(l<r)
    {
        int mid=(l+r)/2;
        tr[now].lc=trlen+1;bt(l,mid);
        tr[now].rc=trlen+1;bt(mid+1,r);
    }
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int mn[110000];
void change(int now,int x,int c)
{
    if(tr[now].l==tr[now].r){tr[now].c=c;return ;}
    
    int mid=(tr[now].l+tr[now].r)/2;
    int lc=tr[now].lc,rc=tr[now].rc;
    
    if(x<=mid)change(lc,x,c);
    else       change(rc,x,c);
    tr[now].c=min(tr[lc].c,tr[rc].c);
}
int getmin(int now,int l,int r)
{
    if(tr[now].l==l&&tr[now].r==r)return tr[now].c;
    
    int mid=(tr[now].l+tr[now].r)/2;
    int lc=tr[now].lc,rc=tr[now].rc;
    
         if(r<=mid)  return getmin(lc,l,r);
    else if(mid+1<=l)return getmin(rc,l,r);
    else return min(getmin(lc,l,mid),getmin(rc,mid+1,r));
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~带修求区间最小~~~~~~~~~~~~~~~~~~~~~~~~~
void add(int now,int x,int v)
{
    if(tr[now].l==tr[now].r){tr[now].v=v;return ;}
    
    int mid=(tr[now].l+tr[now].r)/2;
    int lc=tr[now].lc,rc=tr[now].rc;
    
    if(x<=mid)add(lc,x,v);
    else       add(rc,x,v);
    tr[now].v=max(tr[lc].v,tr[rc].v);
}
int FindCloser(int now,int l,int r)
{
    if(l>r)return 0;
    if(tr[now].l==l&&tr[now].r==r)return tr[now].v;
    
    int mid=(tr[now].l+tr[now].r)/2;
    int lc=tr[now].lc,rc=tr[now].rc;
    
         if(r<=mid)  return FindCloser(lc,l,r);
    else if(mid+1<=l)return FindCloser(rc,l,r);
    else return max(FindCloser(lc,l,mid),FindCloser(rc,mid+1,r));
}
//~~~~~~~~~~~~~~~~找到第一个比当前点大/小的点(维护单调性)~~~~~~~~~~~~~~~~~~~~~~~~

//------------------------------------------------------------------

int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    int n,Q;
    scanf("%d%d",&n,&Q);
    for(int i=1;i<=n;i++)
        scanf("%d",&c[i]), ls[++lslen]=c[i];
    sort(ls+1,ls+lslen+1);
    lslen=unique(ls+1,ls+lslen+1)-ls-1;
    for(int i=1;i<=n;i++)
        g[i]=lower_bound(ls+1,ls+lslen+1,c[i])-ls;
    for(int i=1;i<=Q;i++)
        scanf("%d%d",&q[i].l,&q[i].r), q[i].id=i;
    sort(q+1,q+Q+1,cmp);
    
    int tp=1,u; trlen=0;bt(1,n);
    memset(mn,63,sizeof(mn));
    for(int i=1;i<=n;i++)
    {
        for(int k=FindCloser(1,g[i]+1,lslen);c[k]>c[i]&&k;k=FindCloser(1,g[i]+1,g[k]-1))//FindFirstMax
            if(mn[k]>c[k]-c[i]) mn[k]=c[k]-c[i], change(1,k,mn[k]);
        
        for(int k=FindCloser(1,1,g[i]-1);c[i]>c[k]&&k;k=FindCloser(1,g[k]+1,g[i]-1))//FindFirstMin
            if(mn[k]>c[i]-c[k]) mn[k]=c[i]-c[k], change(1,k,mn[k]);
        add(1,g[i],i);
        
        while(tp<=Q&&q[tp].r==i)
        {
            as[q[tp].id]=getmin(1,q[tp].l,q[tp].r);
            tp++;
        }
    }
    
    for(int i=1;i<=Q;i++)printf("%d\n",as[i]);
    return 0;
}

 

posted @ 2018-10-18 10:56  AKCqhzdy  阅读(330)  评论(0编辑  收藏  举报