题解-TIOJ1905 最理想的身高差

Problem

题目原型

题目大意:求区间最小差值

序列长度$1e5$,询问$2e5$

Solution

总体思路就是找出所有可能作为答案的点对,用资料结构$_(:зゝ∠)_$维护,然后询问

至于如何找出所有可能作为答案的点对,我们先考虑左边比右边小的点对,考虑枚举点对左端点,那么可能作为答案的右端点一定是递减的

其次,若当前左端点为$l$,值为$x$,从左至右的右端点$r$的值依次为${v_1,v_2,\cdots ,v_k}$,则一定存在$\frac 12(x+v_i)> v_{i+1}$,因为如果$\frac 12(x+v_i)\leq v_{i+1}$,则在区间$[l,r_{i+1}]$中,点对$(l,r_i)$或$(r_i,r_{i+1}$一定会更优

所以对于一个固定的左端点,$v_i-x$每次会折半,所以右端点的数量不超过$log_2(10^9)\approx 30$个

对于左边比右边大的点对处理也如法炮制,即对于整个区间来说,可能作为答案的点对数量不超过$60n$


如何快速查找在区间$[r_i,n]$中数值范围在$[x,\frac 12(x+r_i)]$中的最靠左的点呢

考虑对所有点权离散化后对点权建主席树,在每棵线段树中维护的是位置集合,即如果要查找上面的东西,就只要在第$x$棵主席树到第$\frac 12(x+r_i)$棵主席树间查找区间$[r_i,n]$的最左边非零节点


我们成功找到了所有可能为答案的点对,现在来回答询问

对序列维护一棵线段树,考虑将所有点对包括询问按照右端点排序,从左往右枚举右端点,如果是答案点对则在线段树中修改点对左端点的值,如果是询问,则查询区间$[l,r]$的最小值


预处理复杂度$O(n\log_2^2n)$,询问复杂度$O((n\log n+m)\log n)$

总体复杂度$O(n\log_2^2n+m\log n)$,其中一个$\log$基本跑不满,时限也开到了$6s$,实测一个点最慢$1.3s$,所以不用担心常数问题

tips:

  • 预处理中可以不用主席树,改用枚举右端点可以使用单调栈加线段树
  • 最后如果用主席树维护的话可以在线询问
  • 有一个$O((n+m)\sqrt n)$的做法,可以问$boshi$,但常数很大,实测一个点最慢$5s$

Code

代码又臭又长,如果想看短代码,可以看看kb的不用主席树代码

#include<bits/stdc++.h>
using namespace std;
#define rg register

template <typename _Tp> inline _Tp read(_Tp&x){
    char c11=getchar();x=0;
    while(!isdigit(c11))c11=getchar();
    while(isdigit(c11))x=x*10+c11-'0',c11=getchar();
    return x;
}

template <typename _Tp> inline void cmax(_Tp&A,_Tp B) {A=(A>B?A:B);}
template <typename _Tp> inline void cmin(_Tp&A,_Tp B) {A=(A<B?A:B);}

const int N=101000,M=1001000,ae=18;
struct Edge{int v,nxt,w;}a[N*ae+M];
int head[N],Head[N];
int v[N],b[N],Ans[M];
int n,m,_;

inline void add(int u,int v,int w){a[++_].v=v,a[_].w=w,a[_].nxt=head[u],head[u]=_;}
inline void Add(int u,int v,int w){a[++_].v=v,a[_].w=w,a[_].nxt=Head[u],Head[u]=_;}

struct tmp_{
    int ps,vl;
    friend inline int operator < (const tmp_&A,const tmp_&B) {return A.vl<B.vl;}
}tnd[N];
struct node{int l,r,v;}seg[N*ae];

    int rt[N],cnt=0;
    inline void Update(int l,int r,int&x,int ps){
        seg[++cnt]=seg[x];x=cnt,++seg[x].v;
        if(l==r)return ;int mid(l+r>>1);
        if(ps<=mid)Update(l,mid,seg[x].l,ps);
        else Update(mid+1,r,seg[x].r,ps);
        return ;
    }
    inline int Query(int l,int r,int x1,int x2,int L,int R){
        if(seg[x2].v-seg[x1].v==0)return 0;
        if(l==r)return l;int mid(l+r>>1),tmp;
        if(L<=l&&r<=R){
            int t=seg[seg[x2].l].v-seg[seg[x1].l].v;
            if(t)return Query(l,mid,seg[x1].l,seg[x2].l,L,R);
            else return Query(mid+1,r,seg[x1].r,seg[x2].r,L,R);
        }
        if(L<=mid)if(tmp=Query(l,mid,seg[x1].l,seg[x2].l,L,R))return tmp;
        if(mid<R)if(tmp=Query(mid+1,r,seg[x1].r,seg[x2].r,L,R))return tmp;
        return 0;
    }

int val[N<<2];

inline void build(int l,int r,int x){
    val[x]=0x3f3f3f3f;
    if(l==r)return ;int mid(l+r>>1);
    build(l,mid,x<<1);build(mid+1,r,x<<1|1);
}

inline void update(int l,int r,int x,int ps,int alpha){
    cmin(val[x],alpha);
    if(l==r)return ;
    int mid(l+r>>1);
    if(ps<=mid)update(l,mid,x<<1,ps,alpha);
    else update(mid+1,r,x<<1|1,ps,alpha);
}

inline int query(int l,int r,int x,int L,int R){
    if(L<=l&&r<=R)return val[x];
    int mid(l+r>>1),res(0x3f3f3f3f);
    if(L<=mid)cmin(res,query(l,mid,x<<1,L,R));
    if(mid<R)cmin(res,query(mid+1,r,x<<1|1,L,R));
    return res;
}

int dl;

void init();void mainwork();
void print();void prework();
int main(){
    init();
    prework();
    mainwork();
    print();
    return 0;
}

void mainwork(){
    read(m);
    for(rg int i=1,x,y;i<=m;++i){
        read(x),read(y);
        Add(y,x,i);
    }
    build(1,n,1);
    for(rg int x=1;x<=n;++x){
        for(rg int i=head[x];i;i=a[i].nxt)
            update(1,n,1,a[i].v,a[i].w);
        for(rg int i=Head[x];i;i=a[i].nxt)
            Ans[a[i].w]=query(1,n,1,a[i].v,x);
    }return ;
}

void prework(){
    for(rg int i=1;i<=n;++i)tnd[i].ps=i,tnd[i].vl=v[i];
    sort(tnd+1,tnd+n+1);
    for(rg int i=1;i<=n;++i){
        if(!rt[tnd[i].vl])rt[tnd[i].vl]=rt[tnd[i].vl-1];
        Update(1,n,rt[tnd[i].vl],tnd[i].ps);
    }
    int las,l,r;
    for(rg int i=1;i^n;++i){
        las=i;
        while(las){
            l=v[i]-1;
            r=las==i?dl:lower_bound(b+1,b+dl+1,b[v[i]]+b[v[las]]>>1)-b;
            if(las^i&&(b[r]<<1)>=b[v[i]]+b[v[las]])--r;if(l>r)break;
            las=Query(1,n,rt[l],rt[r],las+1,n);
            if(las)add(las,i,b[v[las]]-b[v[i]]);
        }
    }
    
    for(rg int i=1;i^n;++i){
        las=i;
        while(las){
            r=v[i];
            l=las==i?1:lower_bound(b+1,b+dl+1,b[v[i]]+b[v[las]]>>1)-b;
            if(las^i&&(b[l]<<1)<=b[v[i]]+b[v[las]])++l;if(l>r)break;
            las=Query(1,n,rt[l-1],rt[r-1],las+1,n);
            if(las)add(las,i,b[v[i]]-b[v[las]]);
        }
    }
    
    return ;
}

void init(){
    read(n);
    for(rg int i=1;i<=n;++i)b[i]=read(v[i]);
    sort(b+1,b+n+1);dl=unique(b+1,b+n+1)-b-1;
    for(rg int i=1;i<=n;++i)v[i]=lower_bound(b+1,b+dl+1,v[i])-b;
    return ;
}

void print(){
    for(rg int i=1;i<=m;++i)
        printf("%d\n",Ans[i]);
    return ;
}

猜你喜欢

转载自www.cnblogs.com/penth/p/9628694.html
今日推荐