【线段树】【积累】牛客练习赛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]);
}

原文地址:https://www.cnblogs.com/kkkek/p/13749109.html