CF765F Souvenirs

Posted smyjr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF765F Souvenirs相关的知识,希望对你有一定的参考价值。

cf

luogu

没有强制在线,所以可以离线,把询问按右端点排序,然后从左往右枚举右端点,并维护左端点为\(1\)\(i\)的区间的答案,然后询问就可以直接取出来

现在优化这个过程.因为是两个元素的最小绝对值,所以先考虑\(i>j,a_i\le a_j\)的贡献,然后把序列和询问端点颠倒过来再做一遍,就能考虑所有情况.那么这个过程可以看成枚举到右端点\(i\),然后找到最大的\(j<i\)满足\(a_i\le a_j\),更新左端点为\(1\)\(j\)的答案,然后继续,找最大的\(j'<j\)满足\(a_i\le a_j'<a_j\),更新左端点为\(1\)\(j'\)的答案...但是还是不优.进一步的,我们加一个限制,我们强制\(a_j-a_j'\le a_j'-a_i\),如果不满足这个条件,我们就让\((j',j)\)这个点对去更新答案,并且不会更劣.这样子做,\(a_j\)\(a_i\)之差每做一次至少减少一半,所以找\(j\)的过程用上可持久化线段树,复杂度就是\(O(nlognlog_\max a_i)\)

注意每次更新的都是一段前缀,所以可以把一个点答案改为后缀最小值,然后线段树单点修改区间查询即可

#include<bits/stdc++.h>
#define LL long long
#define uLL unsigned long long
#define db double

using namespace std;
const int N=3e5+10;
int rd()

    int x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9')if(ch=='-') w=-1;ch=getchar();
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48);ch=getchar();
    return x*w;

int n,m,q;
int s[N*50],ch[N*50][2],rt[N],tt;
void inst(int o1,int o2,int x,int y)

    s[o1]=max(s[o2],y);
    int l=1,r=m;
    while(l<r)
    
        int mid=(l+r)>>1;
        if(x<=mid)
        
            ch[o1][0]=++tt,ch[o1][1]=ch[o2][1];
            o1=ch[o1][0],o2=ch[o2][0];
            r=mid;
        
        else
        
            ch[o1][0]=ch[o2][0],ch[o1][1]=++tt;
            o1=ch[o1][1],o2=ch[o2][1];
            l=mid+1;
        
        s[o1]=max(s[o2],y);
    

int quer(int o,int l,int r,int ll,int rr)

    if(!o||ll>rr) return 0;
    if(ll<=l&&r<=rr) return s[o];
    int an=0,mid=(l+r)>>1;
    if(ll<=mid) an=max(an,quer(ch[o][0],l,mid,ll,rr));
    if(rr>mid) an=max(an,quer(ch[o][1],mid+1,r,ll,rr));
    return an;

int mi[N<<2];
void psup(int o)mi[o]=min(mi[o<<1],mi[o<<1|1]);
void modif(int o,int l,int r,int lx,int x)

    if(l==r)mi[o]=min(mi[o],x);return;
    int mid=(l+r)>>1;
    if(lx<=mid) modif(o<<1,l,mid,lx,x);
    else modif(o<<1|1,mid+1,r,lx,x);
    psup(o);

int q2(int o,int l,int r,int ll,int rr)

    if(ll<=l&&r<=rr) return mi[o];
    int an=1<<30,mid=(l+r)>>1;
    if(ll<=mid) an=min(an,q2(o<<1,l,mid,ll,rr));
    if(rr>mid) an=min(an,q2(o<<1|1,mid+1,r,ll,rr));
    return an;

int a[N],b[N],an[N];
struct QR

    int l,r,i;
    bool operator < (const QR &bb) const return r<bb.r;
qq[N];
void wk()

    while(tt)
    
        s[tt]=ch[tt][0]=ch[tt][1]=0;
        --tt;
    
    memset(mi,0x3f3f3f,sizeof(mi));
    for(int i=1;i<=n;++i)
        inst(rt[i]=++tt,rt[i-1],a[i],i);
    for(int i=1,j=1;i<=n;++i)
    
        int mx=1<<30,pp=i-1;
        while(1)
        
            int rr=upper_bound(b+1,b+m+1,mx)-b-1,k=quer(rt[pp],1,m,a[i],rr);
            if(!k) break;
            pp=k-1;
            mx=b[a[k]];
            modif(1,1,n,k,mx-b[a[i]]);
            if(mx==b[a[i]]) break;
            mx=(mx-b[a[i]]==1)?b[a[i]]:mx-(mx-b[a[i]])/2;
        
        while(j<=q&&qq[j].r==i)
        
            an[qq[j].i]=min(an[qq[j].i],q2(1,1,n,qq[j].l,qq[j].r));
            ++j;
        
    


int main()

    n=rd();
    for(int i=1;i<=n;++i) a[i]=b[i]=rd();
    sort(b+1,b+n+1),m=unique(b+1,b+n+1)-b-1;
    b[++m]=1<<30|1;
    for(int i=1;i<=n;++i) a[i]=lower_bound(b+1,b+m+1,a[i])-b;
    q=rd();
    for(int i=1;i<=q;++i)
    
        an[i]=1<<30;
        qq[i].l=rd(),qq[i].r=rd(),qq[i].i=i;
    
    sort(qq+1,qq+q+1);
    wk();
    reverse(a+1,a+n+1);
    for(int i=1;i<=q;++i)
    
        qq[i].l=n-qq[i].l+1,qq[i].r=n-qq[i].r+1;
        swap(qq[i].l,qq[i].r);
    
    sort(qq+1,qq+q+1);
    wk();
    for(int i=1;i<=q;++i) printf("%d\n",an[i]);
    return 0; 

以上是关于CF765F Souvenirs的主要内容,如果未能解决你的问题,请参考以下文章

codeforces 765F Souvenirs

codeforces765F Souvenirs

Codeforces 765F Souvenirs - 莫队算法 - 链表

Souvenirs CodeForces - 765F (线段树好题)

CF765FSouvenirs 主席树

cf765F.Souvenirs