题解:P11406 [RMI 2020] 零和 / Sum Zero

【数据删除】卡空间题。

思路

首先极小的和为零的区间总共只有 \(O(n)\) 个。可以直接预处理。

然后从询问右端点开始,每次贪心的取左端点最近的极小区间一定不劣。于是预处理出每个数左边的第一个极小区间左端点,求出每次询问对应的左端点集合,然后二分即可。

考虑如何求出每次询问的左端点集合。因为每个点决策固定,于是可以形成一棵树,直接在这颗树上 dfs,维护到根链即为所求。

于是把询问离线挂到右端点上即可。

实现细节

本题极为卡空间。实现时注意以下几点:

  1. 注意空间复用。
  2. dfs 会带来额外的空间开销。
  3. 不要用 STL。
  4. 由于 \(n\) 的值域仅为 \(4\times10^5\),可以考虑用 \(1\) 个 long long 表示 \(3\) 个值域为 \(n\) 的数。

代码

#include <bits/stdc++.h>
using namespace std;
constexpr int N=4e5+5;
int n,q,pre[N],rk[N],tot,cnt,top,i=1,a1[N],a2[N];
long long t[N],b[N];
void add(int x,int y){
    pre[++cnt]=a1[x];
    t[cnt]=y,a1[x]=cnt;
}
void dfs0(int p){
    t[p]|=1ll<<40,rk[p]=++tot;
    for(int i=a1[p];i;i=pre[i])dfs0(t[i]&((1<<20)-1));
}
void dfs(int p){
    t[p]|=1ll<<40;
    for(;i<=q&&(b[i]>>20&((1<<20)-1))==p;i++)a2[b[i]&((1<<20)-1)]=rk+top+1-lower_bound(rk+1,rk+top+1,b[i]>>40);
    rk[++top]=p+1;
    for(int i=a1[p];i;i=pre[i])dfs(t[i]&((1<<20)-1));
    top--;
}
signed main(){
    clock_t _st=clock();
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a2[i],a1[i]=a1[i-1];
        if(1ll*a2[i-1]+a2[i]>INT_MAX)a1[i]++,a2[i]=1ll*a2[i-1]+a2[i]-INT_MAX-1;
        else if(1ll*a2[i-1]+a2[i]<0)a1[i]--,a2[i]=1ll*a2[i-1]+a2[i]+INT_MAX+1;
        else a2[i]=a2[i-1]+a2[i];
    }
    for(int i=1;i<=n;i++)t[i]=(INT_MAX+1ll)*a1[i]+a2[i];
    sort(t+1,t+n+1);
    for(int i=0;i<=n;i++){
        int l=1,r=n;
        while(l<r){
            const int mid=l+r>>1;
            if(t[mid]>=(INT_MAX+1ll)*a1[i]+a2[i])r=mid;
            else l=mid+1;
        }
        a1[i]=r;
    }
    memset(t,-1,sizeof(t));
    pre[0]=-1,t[a1[0]]=0;
    for(int i=1;i<=n;i++)pre[i]=max(1ll*pre[i-1],t[a1[i]]),t[a1[i]]=i;
    memset(a1,0,sizeof(a1));
    for(int i=1;i<=n;i++)if(~pre[i])add(pre[i],i);
    cin>>q;
    for(int i=1;i<=q;i++){
        long long l,r;cin>>l>>r;
        b[i]=l<<40|r<<20|i;
    }
    for(int i=0;i<=n;i++)t[i]&=(1<<20)-1;
    for(int i=0;i<=n;i++)if(!(t[i]>>40))dfs0(i);
    sort(b+1,b+q+1,[](long long x,long long y){return rk[x>>20&((1<<20)-1)]<rk[y>>20&((1<<20)-1)];});
    for(int i=0;i<=n;i++)t[i]&=(1<<20)-1;
    for(int i=0;i<=n;i++)if(!(t[i]>>40))dfs(i);
    for(int i=1;i<=q;i++)cout<<a2[i]<<'\n';
    clock_t _ed=clock();
    cerr<<(_ed-_st)*1.0/CLOCKS_PER_SEC<<'\n';
    return 0;
}
posted @ 2026-04-20 20:16  Redolent  阅读(2)  评论(0)    收藏  举报