[CF765F]Souvenirs

来源:互联网 发布:30多了能做网络主播吗? 编辑:程序博客网 时间:2024/05/22 03:06

题目大意

给定一个序列
多次询问一个区间最接近两个数的差值
最接近的两个数可以相同,但不能是同一个位置上的数。
允许离线

强大线段树做法

我们首先可以扫描线
从左到右扫,每次处理右端点在扫描线上的所有询问。
我们维护一颗线段树,线段树每个节点的值没有太多实际的意义,但它需要满足一个性质:
假如当前扫描线在now,那么在线段树中查询[l,now]这个区间的答案就是正确的答案
听起来很不可思议?
这样每次询问直接询问即可
我们思考每次从now-1到now,线段树哪些信息需要更改,此时我们加入了一个a[now],设为d。
我们可以先去定位区间[1,now],规定优先走右儿子。
对于一个被定位的区间[L,R]满足R<=now,什么情况下不需要在对它包括它子树的信息进行更改了呢?
因为我们优先走右儿子,那么我们可以维护出一个mi表示此时[R+1,now]的答案。
假如对于[L,R],d在[L,R]中找到的每一个数t(注意如果R=now,t并不能去a[now]),都有|dt|>=mi,那么这个点上的值不更改也可以满足我们线段树需要满足的条件,即询问[i,now]得到正确的答案。于是我们更新mi后直接退出。
否则的话,我们可能要更改这个区间上的值,我们暴力递归左右,这里也是优先走右边。
为了验证是否还需要往下做,每个区间维护一个set,每次给d找前驱和后继即可。
因为不能包含a[now],所以每次做完修改再把now所处的区间的set里都塞一个a[now]。
这样做正确性是没有问题的,时间复杂度如何呢?
我们只需要考虑一直递归到叶子的位置,其他的都是区间,区间定位的复杂度会和递归到叶子保持一致(假设相邻两个递归到叶子的位置i和j,那么在到i叶子路径和到j叶子路径第一次分开后,i那条每往左走右边那个区间被定位为[i+1,j-1]里的直接退出,j那条同理,可以看出两者复杂度同阶)。
一个位置i什么时候会被递归到叶子,显然是[i,now]的答案比[i+1,now]优秀。考虑最坏情况,就是序列1 n n/2 n/4 n/8……每次值会减一半,可以分析出一个位置只会被递归到叶子log次!
算上set的复杂度,感觉应该是三个log?实际跑起来会比较玄学。

#include<cstdio>#include<algorithm>#include<cmath>#include<set>#define fo(i,a,b) for(i=a;i<=b;i++)using namespace std;const int maxn=100000+10,maxm=300000+10,inf=2000000000;struct dong{    int l,r,id;} ask[maxm];int tree[maxn*4],ans[maxm],a[maxn],sta[80];set<int> e[maxn*4];int i,j,k,l,r,t,n,m,mi,top;int read(){    int x=0,f=1;    char ch=getchar();    while (ch<'0'||ch>'9'){        if (ch=='-') f=-1;        ch=getchar();    }    while (ch>='0'&&ch<='9'){        x=x*10+ch-'0';        ch=getchar();    }    return x*f;}bool cmp(dong a,dong b){    return a.r<b.r;}void build(int p,int l,int r){    tree[p]=inf;    if (l==r) return;    int mid=(l+r)/2;    build(p*2,l,mid);build(p*2+1,mid+1,r);}void change(int p,int l,int r,int qr,int d){    if (l==r){        if (l==qr) return;        tree[p]=min(tree[p],abs(d-a[l]));        mi=min(mi,tree[p]);        return;    }    if (r<=qr){        set<int>::iterator it=e[p].lower_bound(d);        if ((it==e[p].end()||abs(*it-d)>=mi)&&(it==e[p].begin()||abs(*(--it)-d)>=mi)){            mi=min(mi,tree[p]);            return;        }        int mid=(l+r)/2;        change(p*2+1,mid+1,r,qr,d);change(p*2,l,mid,qr,d);        tree[p]=min(tree[p*2],tree[p*2+1]);        return;    }    int mid=(l+r)/2;    if (qr<=mid) change(p*2,l,mid,qr,d);    else{        change(p*2+1,mid+1,r,qr,d);        change(p*2,l,mid,qr,d);    }    tree[p]=min(tree[p*2],tree[p*2+1]);}void cr(int p,int l,int r,int a,int b){    e[p].insert(b);    if (l==r) return;    int mid=(l+r)/2;    if (a<=mid) cr(p*2,l,mid,a,b);else cr(p*2+1,mid+1,r,a,b);}int query(int p,int l,int r,int a,int b){    if (l==a&&r==b) return tree[p];    int mid=(l+r)/2;    if (b<=mid) return query(p*2,l,mid,a,b);    else if (a>mid) return query(p*2+1,mid+1,r,a,b);    else return min(query(p*2,l,mid,a,mid),query(p*2+1,mid+1,r,mid+1,b));}void write(int x){    if (!x){        putchar('0');        putchar('\n');        return;    }    top=0;    while (x){        sta[++top]=x%10;        x/=10;    }    while (top){        putchar('0'+sta[top]);        top--;    }    putchar('\n');}int main(){    //freopen("data.in","r",stdin);    n=read();    fo(i,1,n) a[i]=read();    build(1,1,n);    m=read();    fo(i,1,m) ask[i].l=read(),ask[i].r=read(),ask[i].id=i;    sort(ask+1,ask+m+1,cmp);    r=0;    fo(i,1,m){        while (r<ask[i].r){            r++;            mi=inf;            change(1,1,n,r,a[r]);            cr(1,1,n,r,a[r]);        }        ans[ask[i].id]=query(1,1,n,ask[i].l,r);    }    fo(i,1,m) write(ans[i]);}
0 0
原创粉丝点击