多次询问 [l, r] 区间和为 0 (或给定数) 的最大长度(莫队)

本文介绍了一种解决区间和为0最大长度问题的高效算法。通过前缀和离散化与双端队列结合权值线段树,实现O(logn)更新和O(1)查询,适用于数据规模在50000内的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:

n n n 个数,有 m m m 个询问,每次询问给出区间 [ l , r ] [l,r] [l,r],回答区间 [ l , r ] [l,r] [l,r] 中和为 0 的最大长度。
数据范围: 1 &lt; = n , m &lt; = 50000 1 &lt;= n,m &lt;= 50000 1<=n,m<=50000

思路: 区间和为 0 ,那么显然 利用前缀和判相等是最好的方法。先将前缀和离散化,然后利用 50000条双端队列,维护每一个前缀和出现的位置。小的放前面,大的放后面。

当 add 或者 del 位置 x 时,就只需要改变 a[x] 的那一条队列即可。

可知 需要维护的东西就是每一种长度出现的次数,以及长度的最大值。
利用权值线段树可以很轻易的维护这个东西。每一次更新都是 O ( l o g n ) O(logn) O(logn),查询$O(1)
(当要求区间和为 k k k 时,就只是在 a [ x ] a[x] a[x] a [ x ] + k a[x] + k a[x]+k a [ x ] − k a[x] - k a[x]k 这三条队列选更新)

!!! 值得注意的一点就是莫队千万不要自以为,比如这个题在 d e l del del函数中,所使用的队列长度并不一定是 >= 2的

Code:

#include<bits/stdc++.h>
#define debug(x) cout << "[" << #x <<": " << (x) <<"]"<< endl
#define pii pair<int,int>
#define clr(a,b) memset((a),b,sizeof(a))
#define rep(i,a,b) for(int i = a;i < b;i ++)
#define pb push_back
#define MP make_pair
#define LL long long
#define ull unsigned LL
#define ls i << 1
#define rs (i << 1) + 1
#define fi first
#define se second
#define ptch putchar
#define CLR(a) while(!(a).empty()) a.pop()

using namespace std;
inline LL read() {
    LL s = 0,w = 1;
    char ch = getchar();
    while(!isdigit(ch)) {
        if(ch == '-')
            w = -1;
        ch = getchar();
    }
    while(isdigit(ch))
        s = s * 10 + ch - '0',ch = getchar();
    return s * w;
}
inline void write(LL x) {
    if(x < 0)
        putchar('-'), x = -x;
    if(x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}

const int maxn = 2e5 + 10;
int Ls[maxn],a[maxn],n,m;
int ans[maxn],belong[maxn];
struct xx {
    int l,r,id;
} Q[maxn];

int cmp(xx a, xx b) {
    return (belong[a.l] ^ belong[b.l]) ?
           belong[a.l] < belong[b.l] :
           ((belong[a.l] & 1) ? a.r < b.r : a.r > b.r);
}

deque<int>deq[maxn];
int maxx[maxn << 2],ok[maxn];

void update(int l,int r,int pos,int i,int val){
    if(l == r){
        ok[l] += val;
        maxx[i] = (ok[l] <= 0 ? 0 : l);
        return ;
    }
    int mid = (l + r) >> 1;
    if(pos <= mid) update(l,mid,pos,ls,val);
    if(pos > mid) update(mid + 1,r,pos,rs,val);
    maxx[i] = max(maxx[ls],maxx[rs]);
}

void del(int x,bool flag) { ///0在首,1在尾
    int fro = deq[a[x]].front();
    int bac = deq[a[x]].back();
    update(1,n,bac - fro,1,-1);

    if(!flag) deq[a[x]].pop_front();
    else deq[a[x]].pop_back();

    if(deq[a[x]].empty()) return ;
    int tmp = (flag == 0 ? deq[a[x]].front() : deq[a[x]].back());
    update(1,n,flag == 0 ? bac - tmp : tmp - fro,1,1);
}

void add(int x,bool flag) {
    if(deq[a[x]].empty()) {
        deq[a[x]].push_front(x);
        return ;
    }
    int fro = deq[a[x]].front();
    int bac = deq[a[x]].back();

    if(!flag) deq[a[x]].push_front(x);
    else deq[a[x]].push_back(x);

    update(1,n,bac - fro,1,-1);
    update(1,n,flag == 0 ? bac - x : x - fro,1,1);
}

int main() {
//#ifndef ONLINE_JUDGE
//    freopen("in.txt", "r", stdin);
//    freopen("out.txt", "w", stdout);
//#endif
    n = read(),m = read();
    int block = sqrt(n);
    rep(i,1,n + 1) {
        a[i] = read();
        a[i] += a[i - 1];
        Ls[i] = a[i];
        belong[i] = (i - 1) / block + 1;
    }
    ++ n; Ls[n] = 0;
    sort(Ls + 1,Ls + 1 + n);
    int en = unique(Ls + 1,Ls + 1 + n) - Ls - 1;
    rep(i,0,n) a[i] = lower_bound(Ls + 1,Ls + 1 + en,a[i]) - Ls;
    rep(i,1,m + 1) {
        Q[i].l = read();
        Q[i].r = read();
        Q[i].id = i;
    }
    sort(Q + 1,Q + m + 1,cmp);
    int L = 1,R = 0;
    deq[a[0]].push_front(0);
    for(int i = 1; i <= m; ++ i) {
        while(L > Q[i].l) -- L,add(L - 1,0);
        while(R < Q[i].r) add(++ R,1);
        while(L < Q[i].l) del(L - 1,0),L ++;
        while(R > Q[i].r) del(R --,1);
        ans[Q[i].id] = maxx[1];
    }
    for(int i = 1; i <= m; ++ i)
        write(ans[i]), ptch('\n');
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值