【BZOJ2221】面试的考验,随机数列+线段树+离线

来源:互联网 发布:holi天气数据来源 编辑:程序博客网 时间:2024/05/17 06:09

传送门
shallwe’s blog //以上内容由shallwe本人暴力添加
思路:
好题
断断续续想了1周左右
暴力思路显然是O(n2logn)
我最开始的想法是用莫队+set来搞
但是发现即使是在随机数列下,O(nnlogn)的复杂度也是不科学的
那怎么办?
(下列做法可能比较难想……)
如果说我们处理出当前区间中每个元素往前的最小差值,那不就可以得到答案了吗?
再深入思考一下
对于a[j]>a[k]>x,j<k,a[j]>a[k]>x,j<ka[j]不贡献答案;
a[j]<a[k]<x

对于这道题来说,这个性质是极好的
这样就相当于每次找某个权值区间中位置小于某个值的最大位置了
但这样并不好找
因为当前点后面的数会对我们查询产生影响
那就从左向右扫着做,后面数就不会有影响了
这样就相当于找某个权值区间中的最大位置了
这就很好办了啊
权值线段树就可以了
操作是单点修改和区间求max
那每次求出的答案的贡献区间是什么呢?
如果说当前点为r,找到的贡献点是l,那么它对答案的贡献区间是左端点在[1,l],右端点在[r,n]的询问
这不是很好维护
所以我们干脆把询问离线下来,以右端点为关键字从小到大排序,然后把所有的答案都记到左端点上,这样就可以拿一颗新的线段树来维护了
每次修改的区间就是[1,l]
操作是区间修改和区间求min
总结一下就是随机数列的神奇性质+离线+扫描线思想+双线段树维护
代码还是挺好写的
题解好难写啊
代码:

#include<cstdio>#include<iostream>#include<algorithm>#define M 100005#define up 2147483647using namespace std;int n,m;int a[M],b[M],ha[M],S[M<<2],V[M<<2],lazy[M<<2],ans[M];struct query{    int id,l,r;}q[M];int in(){    int t=0;char ch=getchar();bool f=0;    while (ch>'9'||ch<'0') f=(ch=='-'),ch=getchar();    while (ch>='0'&&ch<='9') t=(t<<1)+(t<<3)+ch-48,ch=getchar();    return f?-t:t;}bool cmp(query a,query b){    return a.r<b.r;}int V_get(int rt,int begin,int end,int l,int r){    if (l<=begin&&end<=r) return V[rt];    int mid=begin+end>>1,ans=0;    if (mid>=l) ans=max(ans,V_get(rt<<1,begin,mid,l,r));    if (mid<r)  ans=max(ans,V_get(rt<<1|1,mid+1,end,l,r));    return ans;}void V_update(int rt,int begin,int end,int pos,int val){    if (begin==end) return void(V[rt]=val);    int mid=begin+end>>1;    if (mid>=pos) V_update(rt<<1,begin,mid,pos,val);    else V_update(rt<<1|1,mid+1,end,pos,val);    V[rt]=val;}void S_build(int rt,int begin,int end){    S[rt]=up;    if (begin==end) return;    int mid=begin+end>>1;    S_build(rt<<1,begin,mid);    S_build(rt<<1|1,mid+1,end);}void pushdown(int rt){    if (!lazy[rt]) return;    S[rt<<1]=min(S[rt<<1],lazy[rt]);        if (!lazy[rt<<1]) lazy[rt<<1]=lazy[rt];        else lazy[rt<<1]=min(lazy[rt],lazy[rt<<1]);    S[rt<<1|1]=min(S[rt<<1|1],lazy[rt]);        if (!lazy[rt<<1|1]) lazy[rt<<1|1]=lazy[rt];        else lazy[rt<<1|1]=min(lazy[rt],lazy[rt<<1|1]);    lazy[rt]=0;}void S_update(int rt,int begin,int end,int l,int r,int val){    if (l<=begin&&end<=r)    {        S[rt]=min(S[rt],val);        if (!lazy[rt]) lazy[rt]=val;        else lazy[rt]=min(val,lazy[rt]);        return;    }    pushdown(rt);    int mid=begin+end>>1;    if (mid>=l) S_update(rt<<1,begin,mid,l,r,val);    if (mid<r) S_update(rt<<1|1,mid+1,end,l,r,val);    S[rt]=min(S[rt<<1],S[rt<<1|1]);}int S_get(int rt,int begin,int end,int l,int r){    if (l<=begin&&end<=r) return S[rt];    pushdown(rt);    int mid=begin+end>>1,ans=up;    if (mid>=l) ans=min(ans,S_get(rt<<1,begin,mid,l,r));    if (mid<r) ans=min(ans,S_get(rt<<1|1,mid+1,end,l,r));    return ans;}void cal(int x){    int y=1,val=b[0],ans=up;    for (;val>a[x]&&y;)    {        y=V_get(1,1,b[0],a[x],val);        if (y&&a[y]!=a[x])            ans=min(ans,ha[a[y]]-ha[a[x]]),            S_update(1,1,n,1,y,ha[a[y]]-ha[a[x]]);        if (!y) break;        val=a[y]-1;    }    val=1;y=1;    for (;val<a[x]&&y;)    {        y=V_get(1,1,b[0],val,a[x]);        if (y&&a[y]!=a[x])            ans=min(ans,ha[a[x]]-ha[a[y]]),            S_update(1,1,n,1,y,ha[a[x]]-ha[a[y]]);        if (!y) break;        val=a[y]+1;    }    V_update(1,1,b[0],a[x],x);}main(){    n=in();m=in();    for (int i=1;i<=n;++i) b[i]=a[i]=in();    sort(b+1,b+n+1);    b[0]=unique(b+1,b+n+1)-b-1;    for (int i=1;i<=b[0];++i) ha[i]=b[i];    for (int i=1;i<=n;++i)        a[i]=lower_bound(b+1,b+b[0]+1,a[i])-b;    for (int i=1;i<=m;++i)        q[i]=(query){i,in(),in()};    sort(q+1,q+m+1,cmp);    int last=0;    S_build(1,1,n);    for (int i=1;i<=m;++i)    {        for (int j=last+1;j<=q[i].r;++j) cal(j);        ans[q[i].id]=S_get(1,1,n,q[i].l,q[i].r);        last=q[i].r;    }    for (int i=1;i<=m;++i) printf("%d\n",ans[i]);}
0 0