[JSOI2009]面试的考验 解题报告

考虑将询问区间按右端点排序。考虑x会与它前面产生贡献的点对,显然,如果 i<j<x,ai>aj>ax ,那么(i,x)就是无用的。也就是说我们要求位置 < x且权值大于 ax 的点和小于 ax 的点的两个类似笛卡尔树的序列的东西,而这个在随机数据中显然是 O(logn) 的。
但是问题是怎么求这个东西呢?一个比较脑残的想法是线段树!就是我们存一棵权值线段树,保存的是权值在一个范围内的编号最靠后的。这样我们每次取一个最靠后的。
然后这样常数巨大跑了10s。。
另一个比较简单的做法是考虑我们要生成大于的那个序列,我们生成到了y,那么实际上我们就是要找小于y的序列中第一个大于 ax 的。而这当然是可以二分的,所以我们只需要 O(nlognloglogn) 就可以生成这个蛋疼的序列了!
但是并不要高兴太早,因为我们还需要支持 O(nlogn) 个插入和 O(q) 个后缀min查询。。这个可以树状数组或分块,跑起来差不多。
时间复杂度就是 O(nlognloglogn+nlog2n+qlogn) O(nlognlogn+nlogn+qn) (求序列的那个 O(lognloglogn) 其实跟 O(log2n) 差别不大)。
这题还有一个很蛋疼的地方就是答案不计0…(估计是出题人写残了吧rofl)。
代码(线段树):

#include<cstdio>
#include<iostream>
using namespace std;
#include<algorithm>
#include<cstring>
#include<cmath>
const int N=1e5+5,Q=1e5+5;

int n;
int hash[N];
int a[N];

int segt[N<<2];
#define lson node<<1,l,l+r>>1
#define rson node<<1|1,(l+r>>1)+1,r
void add(int node,int l,int r,int x){
    segt[node]=x;
    if(l==r)return;
    if(a[x]<=l+r>>1)add(lson,x);
    else add(rson,x);
}
int query(int node,int l,int r,int L,int R){
    if(L<=l&&r<=R)return segt[node];
    int ans=0;
    if(L<=l+r>>1)ans=max(ans,query(lson,L,R));
    if(R>l+r>>1)ans=max(ans,query(rson,L,R));
    return ans;
}

#define inf 0x7fffffff
int bit[N];
void add(int x,int A){
    //cout<<"add:"<<x<<","<<A<<endl;
    for(;x;x-=x&-x)bit[x]=min(bit[x],A);
}
int query(int x){
    //cout<<"query("<<x<<")\n";
    int ans=inf;
    for(;x<=n;x+=x&-x){
        ans=min(ans,bit[x]);
        //cout<<"bit["<<x<<"]="<<bit[x]<<endl;
    }
    return ans;
}

struct QS{
    int l,r,i;
    bool operator < (const QS & o)const{
        return r<o.r;
    }
}que[Q];
int ans[Q];
int main(){
    int q;
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;++i){
        scanf("%d",a+i);
        hash[i]=a[i];
    }
    sort(hash+1,hash+n+1);
    int htot=unique(hash+1,hash+n+1)-hash;
    for(int i=n;i;--i)a[i]=lower_bound(hash+1,hash+htot,a[i])-hash;
    --htot;
    for(int i=0;i<q;++i){
        scanf("%d%d",&que[i].l,&que[i].r);
        que[i].i=i;
    }
    sort(que,que+q);
    que[q].r=-1;

    memset(bit,127,sizeof(bit));
    int x;
    for(int i=1,j=0;i<=n;++i){
        for(x=query(1,1,htot,a[i],htot);x&&a[x]!=a[i];){
            add(x,hash[a[x]]-hash[a[i]]);
            x=query(1,1,htot,a[i],a[x]-1);
        }
        for(x=query(1,1,htot,1,a[i]);x&&a[x]!=a[i];){
            add(x,hash[a[i]]-hash[a[x]]);
            x=query(1,1,htot,a[x]+1,a[i]);
        }
        add(1,1,htot,i);
        for(;que[j].r==i;++j)ans[que[j].i]=query(que[j].l);
    }
    for(int i=0;i<q;++i)printf("%d\n",ans[i]);
}

代码(bit):

#include<cstdio>
#include<iostream>
using namespace std;
#include<algorithm>
#include<cstring>
#include<cmath>
const int N=1e5+5,Q=1e5+5;

int n;
int hash[N];
int a[N];

int big[N][30],sml[N][30];

#define inf 0x7fffffff
int bit[N];
void add(int x,int A){
    //cout<<"add:"<<x<<","<<A<<endl;
    for(;x;x-=x&-x)bit[x]=min(bit[x],A);
}
int query(int x,int n){
    //cout<<"query("<<x<<")\n";
    int ans=inf;
    for(;x<=n;x+=x&-x){
        ans=min(ans,bit[x]);
        //cout<<"bit["<<x<<"]="<<bit[x]<<endl;
    }
    return ans;
}

struct QS{
    int l,r,i;
    bool operator < (const QS & o)const{
        return r<o.r;
    }
}que[Q];
int ans[Q];
int main(){
    int q;
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;++i){
        scanf("%d",a+i);
        hash[i]=a[i];
    }
    sort(hash+1,hash+n+1);
    int htot=unique(hash+1,hash+n+1)-hash;
    for(int i=n;i;--i)a[i]=lower_bound(hash+1,hash+htot,a[i])-hash;
    --htot;
    for(int i=0;i<q;++i){
        scanf("%d%d",&que[i].l,&que[i].r);
        que[i].i=i;
    }
    sort(que,que+q);
    que[q].r=-1;

    int tmp=0;

    memset(bit,127,sizeof(bit));
    bool flag;
    int x;
    for(int i=1,j=0;i<=n;++i){
        //printf("-------%d------\n",i);

        flag=0;
        for(int k=i;--k;)
            if(a[k]>a[i]){
                big[i][++big[i][0]]=k;
                flag=1;
                break;
            }
        for(x=big[i][1];flag;){
            flag=0;
            for(int k=1;k<=sml[x][0];++k)
                if(a[sml[x][k]]>a[i]){
                    big[i][++big[i][0]]=sml[x][k];
                    flag=1;
                    x=sml[x][k];
                    break;
                }
        }

        flag=0;
        for(int k=i;--k;)
            if(a[k]<a[i]){
                sml[i][++sml[i][0]]=k;
                flag=1;
                break;
            }
        for(x=sml[i][1];flag;){
            flag=0;
            for(int k=1;k<=big[x][0];++k)
                if(a[big[x][k]]<a[i]){
                    sml[i][++sml[i][0]]=big[x][k];
                    x=big[x][k];
                    flag=1;
                    break;
                }
        }

        tmp=max(tmp,max(big[i][0],sml[i][0]));

        for(int k=big[i][0];k;--k)add(big[i][k],hash[a[big[i][k]]]-hash[a[i]]);
        for(int k=sml[i][0];k;--k)add(sml[i][k],hash[a[i]]-hash[a[sml[i][k]]]);

        for(;que[j].r==i;++j)ans[que[j].i]=query(que[j].l,i);
    }
    for(int i=0;i<q;++i)printf("%d\n",ans[i]);
}

代码(分块):

#include<cstdio>
#include<iostream>
using namespace std;
#include<algorithm>
#include<cstring>
#include<cmath>
const int N=1e5+5,Q=1e5+5;

int n;
int hash[N];
int a[N];

int big[N][30],sml[N][30];

#define inf 0x7fffffff
const int S=400;
int block[N/S+5];
int b[N];
void add(int x,int A){
    b[x]=min(b[x],A);
    block[x/S]=min(block[x/S],A);
}
int query(int l,int r){
    int ans=inf;
    for(int i=l/S,rs=r/S;++i<=rs;)ans=min(ans,block[i]);
    for(int i=min((l/S+1)*S,r+1);--i>=l;)ans=min(ans,b[i]);
    return ans;
}

struct QS{
    int l,r,i;
    bool operator < (const QS & o)const{
        return r<o.r;
    }
}que[Q];
int ans[Q];
int main(){
    int q;
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;++i){
        scanf("%d",a+i);
        hash[i]=a[i];
    }
    sort(hash+1,hash+n+1);
    int htot=unique(hash+1,hash+n+1)-hash;
    for(int i=n;i;--i)a[i]=lower_bound(hash+1,hash+htot,a[i])-hash;
    --htot;
    for(int i=0;i<q;++i){
        scanf("%d%d",&que[i].l,&que[i].r);
        que[i].i=i;
    }
    sort(que,que+q);
    que[q].r=-1;

    int tmp=0;

    memset(b,127,sizeof(b));
    memset(block,127,sizeof(block));
    bool flag;
    int x;
    for(int i=1,j=0;i<=n;++i){
        //printf("-------%d------\n",i);

        flag=0;
        for(int k=i;--k;)
            if(a[k]>a[i]){
                big[i][++big[i][0]]=k;
                flag=1;
                break;
            }
        for(x=big[i][1];flag;){
            flag=0;
            for(int k=1;k<=sml[x][0];++k)
                if(a[sml[x][k]]>a[i]){
                    big[i][++big[i][0]]=sml[x][k];
                    flag=1;
                    x=sml[x][k];
                    break;
                }
        }

        flag=0;
        for(int k=i;--k;)
            if(a[k]<a[i]){
                sml[i][++sml[i][0]]=k;
                flag=1;
                break;
            }
        for(x=sml[i][1];flag;){
            flag=0;
            for(int k=1;k<=big[x][0];++k)
                if(a[big[x][k]]<a[i]){
                    sml[i][++sml[i][0]]=big[x][k];
                    x=big[x][k];
                    flag=1;
                    break;
                }
        }

        tmp=max(tmp,max(big[i][0],sml[i][0]));

        for(int k=big[i][0];k;--k)add(big[i][k],hash[a[big[i][k]]]-hash[a[i]]);
        for(int k=sml[i][0];k;--k)add(sml[i][k],hash[a[i]]-hash[a[sml[i][k]]]);

        for(;que[j].r==i;++j)ans[que[j].i]=query(que[j].l,i);
    }
    for(int i=0;i<q;++i)printf("%d\n",ans[i]);
}


总结:
①对于大O复杂度的东西开数组时一定要想清常数。
②要注意划归子问题,利用已求的东西。

你可能感兴趣的:(线段树,bit,分块,特殊数据)