zoukankan      html  css  js  c++  java
  • 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
    ",as[i]);
        return 0;
    }
  • 相关阅读:
    DNS 原理入门
    DiG HOWTO How to use dig to query DNS name servers.
    Top 10 Free Wireless Network hacking/monitoring tools for ethical hackers and businesses
    LoadRunner:视频教程、课件
    Android:开发环境搭建相关问题
    Android:使用代理服务器安装SDKs
    JavaSe:Cookie 管理的API介绍
    使用 Eclipse 玩转 C、C++
    C++: 主要知识点
    C、C++: 引用、指针、实例、内存模型、namespace
  • 原文地址:https://www.cnblogs.com/AKCqhzdy/p/9809230.html
Copyright © 2011-2022 走看看