• 【线段树】【积累】牛客练习赛70 F曲调


    【线段树】【积累】牛客练习赛70 F曲调

    题意:

    给定一个长度为 (n) 的序列 (a)(nleq 100000,\,|a_i|leq10^9) ,有 (m) 个询问 。 每个询问给出 (l,r) ,要求区间 ([l,r]) 绝对值最小的子段和(子段不为空)。

    叨叨:

    打练习赛的时候考虑了用莫队离散,然后用线段树+set查询和修改记录,但是莫队需要维护 (l)(r) ,但是修改会相互影响之前的值,太难实现了。

    完全没想到固定一个端点的做法qaq。想到了也想不出题解这种复杂度的做法。

    到底还是因为没习惯这种套路。

    题解:

    对询问以 (r) 递增排序,然后枚举 (r) 来更新以 (r) 为右端点的所有区间的答案。

    预先处理数组的前缀和,记为 (sum)

    对于当前 (x=r) ,求 最大的 (x_1) 满足 (x_1<x) 使得 (sum_{x{1}}leq sum_x) ,然后对满足 (lleq x_1+1)(l) 的更新答案 (min(ans_l,sum_x-sum_{x_1})) ,再求最大的 (x_2) 满足 (x_2<x_1) 使得 (sum_{x_1}leq sum_{x_2}leq sum_x) ,然后对满足 (lleq{x_2+1})(l) 更新答案 (min(ans_l,sum_x-sum_{x_2}))。按照上面步骤执行下去,直到 (sum_x=sum_{x_i})(x_i=1) 为止。

    此时每个点寻找 (x) 的次数最糟会达 (mathcal{O(n)}) ,需要优化。

    可以发现,每当寻找点 (x_i) ,若 (sum_{x_i}-sum_{x_{i-1}}leq sum_x-sum_{xi}) ,则该 (x_i) 对答案无贡献,所以每次可以只寻找满足 (lceilfrac{sum_x+sum_{x_{i-1}}}2 ceilleq sum_{x_i}leq sum_x) 的最大 (x_i) 即可,每次寻找不会超过上一次的一般,总共需要寻找 (mathcal{O}(W))(W)(sum) 的值域。

    对于 (sum_{x_1}geq sum_x) 用相同方法再处理一遍。

    用主席树寻找 (x_i) ,用线段树维护 (min) ,时间复杂度 (mathcal{O}(n\,logn\,logW))

    代码:

    #include<bits/stdc++.h>
    #define mem(a,b) memset(a,b,sizeof(a))
    #define mkp(a,b) make_pair(a,b)
    using namespace std;
    typedef long long ll;
    const int maxn=5e5+5;
    const int inf=0x3f3f3f3f;
    const int mod=998244353;
    int n,m;
    ll h[maxn];int cnt;
    inline int id(ll x){return lower_bound(h+1,h+1+cnt,x)-h;}
    int a[maxn];ll sum[maxn];
    struct Q{
        int l,r,id;
        bool operator<(const Q&p)const{return r<p.r;}
    }q[maxn];
    int T[maxn],L[maxn<<2],R[maxn<<2],num[maxn<<2],tnt;
    void update(int &rt,int pre,int l,int r,int x)
    {
        if(!rt||rt==pre){
            rt=++tnt;L[rt]=L[pre];R[rt]=R[pre];num[rt]=num[pre];
        }
        num[rt]++;
        if(l==r)return;
        int mid=(l+r)>>1;
        if(x<=mid)update(L[rt],L[pre],l,mid,x);
        else update(R[rt],R[pre],mid+1,r,x);
    }
    int qma(int rt,int pre,int l,int r,int ql,int qr)
    {
        if(!(num[rt]-num[pre]))return 0;
        if(l==r)return l;
        int mid=(l+r)>>1,tres=0;
        if(qr>mid&&(num[R[rt]]-num[R[pre]]))tres=qma(R[rt],R[pre],mid+1,r,ql,qr);
        if(tres)return tres;
        return qma(L[rt],L[pre],l,mid,ql,qr);
    }
    const ll UP=1e16;
    ll mi[maxn<<2],ly[maxn<<2];
    void build(int k,int l,int r)
    {
        ly[k]=UP;
        if(l==r){
            mi[k]=abs(a[l]);return;
        }
        int mid=(l+r)>>1;
        build(k<<1,l,mid);build(k<<1|1,mid+1,r);
        mi[k]=min(mi[k<<1],mi[k<<1|1]);
    }
    void down(int k)
    {
        ly[k<<1]=min(ly[k<<1],ly[k]);ly[k<<1|1]=min(ly[k<<1|1],ly[k]);ly[k]=UP;
        mi[k<<1]=min(mi[k<<1],ly[k<<1]);mi[k<<1|1]=min(mi[k<<1|1],ly[k<<1|1]);
    }
    void update(int k,int l,int r,int ql,int qr,ll v)
    {
        if(ql>r||qr<l)return;
        if(ql<=l&&r<=qr){
            mi[k]=min(mi[k],v);ly[k]=min(ly[k],v);
            return;
        }
        if(ly[k]!=UP)down(k);
        int mid=(l+r)>>1;
        update(k<<1,l,mid,ql,qr,v);update(k<<1|1,mid+1,r,ql,qr,v);
        mi[k]=min(mi[k<<1],mi[k<<1|1]);
    }
    ll query(int k,int l,int r,int ql,int qr)
    {
        if(ql>r||qr<l)return UP;
        if(ql<=l&&r<=qr)return mi[k];
        if(ly[k]!=UP)down(k);
        int mid=(l+r)>>1;
        return min(query(k<<1,l,mid,ql,qr),query(k<<1|1,mid+1,r,ql,qr));
    }
    void solve(int r)
    {
        ll sr=sum[r],sl=h[1];int t,rr=r;
        update(1,1,n,1,1,abs(sr));
        while(sr>sl&&rr>1)
        {
            t=qma(T[id(sr)],T[id(sl)-1],1,n,1,rr-1);
    //        printf("(%lld %lld %d %d %lld) ",sl,sr,rr,t,sum[t]);
            if(!t)break;
            sl=sum[t];rr=t;
            update(1,1,n,1,t+1,sr-sl);
            sl=(sl+sr+1)>>1;
        }
        sr=sum[r];sl=h[cnt];rr=r;
        while(sr<sl&&rr>1)
        {
            t=qma(T[id(sl)],T[id(sr)-1],1,n,1,rr-1);
    //    	printf("(%lld %lld %d %d %lld) ",sl,sr,rr,t,sum[t]);
            if(!t)break;
            sl=sum[t];rr=t;
            update(1,1,n,1,t+1,sl-sr);
            sl=(sl+sr+1)>>1;
        }
    }
    struct TT{
        ll v;int id;
        bool operator<(const TT&p)const{return v<p.v;}
    }pt[maxn];
    ll res[maxn];
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            sum[i]=sum[i-1]+a[i];
            h[++cnt]=sum[i];
            pt[i]={sum[i],i};
        }
        build(1,1,n);
        sort(h+1,h+1+cnt);
        cnt=unique(h+1,h+1+cnt)-h-1;
        sort(pt+1,pt+1+n);
        for(int i=1,idd;i<=n;i++){
            idd=id(pt[i].v);
            update(T[idd],T[idd-1],1,n,pt[i].id);
        }
        for(int i=1,l,r;i<=m;i++)
        {
            scanf("%d%d",&l,&r);
            q[i]={l,r,i};
        }
        sort(q+1,q+1+m);
        int st=0;
        for(int i=1;i<=m;i++)
        {
            while(st<q[i].r)solve(++st);
            res[q[i].id]=query(1,1,n,q[i].l,q[i].r);
        }
        for(int i=1;i<=m;i++)printf("%lld
    ",res[i]);
    }
    
    
  • 相关阅读:
    iOS
    H5
    MongoDB应用场景
    win10虚拟桌面;一不小心按错了突然只剩下桌面,启动的程序都没了
    Java中Pattern类的quote方法将任何字符串(包括正则表达式)都转换成字符串常量,不具有任何匹配功能
    MongoDB模糊查询,以及MongoDB模糊查询时带有括号的情况
    对一个结果集(List)进行手动分页
    史上最污技术解读
    Java编译时多态和运行时多态
    MongoDB常用查询,排序,group,SpringDataMongoDB update group
  • 原文地址:https://www.cnblogs.com/kkkek/p/13749109.html
Copyright © 2020-2023  润新知