📌  相关文章
📜  查找具有更新的至少X值的[L,R]范围内的最小索引(1)

📅  最后修改于: 2023-12-03 15:26:36.692000             🧑  作者: Mango

查找具有更新的至少X值的[L,R]范围内的最小索引

在程序开发中,经常需要查找具有更新的至少X值的[L,R]范围内的最小索引。以下是介绍查找具有更新的至少X值的[L,R]范围内的最小索引的一些重要信息和代码示例。

1. 问题描述

问题描述如下:
给定一个长度为N的序列A和一个整数X,查询满足如下要求的最小的i:

  • A[i]>=X。
  • [L,i]间的元素相对于[1,i]间的元素,有至少X个元素被修改过。
2. 经典算法

这个问题可以使用经典算法解决,时间复杂度为O(NlogN)。

2.1 离线查询
  • 预处理:我们将每个被修改的位置插入线段树中并存储该位置修改的值。同时,我们将所有询问按照右端点从小到大排序。我们将idx[i]用于存储第i个询问对应的当前i的位置。
  • 查询:对于每个询问i,我们从输入序列的下一个未处理位置开始,将所有修改序列的元素插入线段树中。然后,我们考虑从idx[i]到右端点,插入输入序列的元素并在插入其父节点之前更新它们的值。然后,我们从线段树根节点开始找到第一个权值不小于X的节点,记录其下标并更新idx[i]为当前位置。
2.2 基于主席树的在线算法
  • 主席树:我们使用主席树维护输入序列的版本。具体而言,树的第i层包括n个节点,表示版本i中输入序列的n个位置。我们使用p[i]来表示节点i对应的版本,这些版本按照时间顺序排序。因此,对于每个版本,节点i的值为version[p[i]][i],其中version[k][i]表示第k个版本中输入序列的第i个位置的值。
  • 预处理:我们将每个被修改的位置插入线段树中并存储该位置修改的值。同时,对于每个元素,我们在对应版本的主席树上构建其对应的节点并将其值设置为1。因此,第i个版本上的输入序列中的第j个位置将等于树的第i层中第j个节点的权值。
  • 查询:对于每个询问i,我们从左到右依次遍历输入序列的位置。然后,我们将输入序列中当前位置在左端点之前所有版本的主席树的根节点插入线段树中。然后,我们插入输入序列中当前位置在该版本中的主席树的节点并在插入其父节点之前更新它的值。然后,我们从线段树根节点开始找到第一个权值不小于X的节点,记录其下标并返回。
3. 代码示例

离线查询的代码示例如下:

int N,M,cnt=0;
LL a[MAXN],o[MAXN],od[MAXN],idx[MAXN];
struct Query{int k,l,r,id;} Q[MAXM];
void add(int x,LL w,int d){for(;x<=N;x+=x&-x)o[x]+=w,od[x]+=d;}
LL get(int x){LL res=0,d=0;for(;x;x-=x&-x)res+=o[x],d+=od[x];return res-d*X;}
bool cmp(Query A,Query B){return A.r==B.r?A.k<B.k:A.r<B.r;}
int main(){
    scanf("%d%d%d",&N,&M,&X);
    for(int i=1;i<=N;i++)scanf("%lld",&a[i]);
    for(int i=1;i<=M;i++){
        Q[i].id=i;scanf("%d%d",&Q[i].l,&Q[i].r);Q[i].k=(Q[i].r-Q[i].l+1)/X;idx[i]=Q[i].l-1;
    }
    sort(Q+1,Q+M+1,cmp);
    for(int i=1,j=1;i<=N;i++){
        while(j<=M&&Q[j].r==i){
            if(idx[Q[j].id]<Q[j].l)idx[Q[j].id]=Q[j].l-1;
            for(int k=idx[Q[j].id]+1;k<=i;k++)add(a[k]+1,1,X-((i-k)/X));
            idx[Q[j].id]=i,j++;
        }
        add(a[i]+1,-1,0);a[i]++,add(a[i]+1,1,0);
    }
    for(int i=1;i<=M;i++){
        LL l=0,r=N-m,ll,rr;
        while(l<=r){
            LL mid=(l+r)>>1,x=get(Q[i].r)-get(mid-1);
            if(x>=X)ll=mid,r=mid-1,rr=x;
            else l=mid+1;
        }
        printf("%lld\n",Q[i].k*X+ll-1);
    }
    return 0;
}

主席树的代码示例如下:

int cur=0;
LL a[MAXN],nxt[MAXN],fir[MAXN],ver[MAXN],idx[MAXN],o[MAXN],od[MAXN],ans[MAXM];
struct Query{int k,l,r,id;} Q[MAXM];
struct Node{int l,r;LL s;}tr[MAXN*20];
int Build(int l,int r){
    int t=++cur;tr[t].s=0;
    if(l<r){int mid=(l+r)>>1;tr[t].l=Build(l,mid),tr[t].r=Build(mid+1,r);}
    return t;
}
int Insert(int p,int l,int r,int x){
    int t=++cur;memcpy(tr+t,tr+p,sizeof(Node));
    if(l==r)tr[t].s=tr[p].s+1;
    else{int mid=(l+r)>>1;if(x<=mid)tr[t].l=Insert(tr[p].l,l,mid,x);else tr[t].r=Insert(tr[p].r,mid+1,r,x);
        tr[t].s=tr[tr[t].l].s+tr[tr[t].r].s;}
    return t;
}
LL Query(int p,int l,int r,int x,int y){
    if(!p||tr[p].s<=y-x)return 0;
    if(l==r)return tr[p].s-y+l;
    int mid=(l+r)>>1;
    if(mid>=x)return Query(tr[p].l,l,mid,x,y)+(tr[tr[p].r].s-y+mid);
    else return Query(tr[p].r,mid+1,r,x,y);
}
bool cmp(Query A,Query B){return A.r==B.r?A.k<B.k:A.r<B.r;}
int main(){
    int N,M,X;scanf("%d%d%d",&N,&M,&X);
    for(int i=1;i<=N;i++)scanf("%lld",&a[i]);
    for(int i=1;i<=M;i++){
        Q[i].id=i;scanf("%d%d",&Q[i].l,&Q[i].r);Q[i].k=(Q[i].r-Q[i].l+1)/X;
    }
    sort(Q+1,Q+M+1,cmp);
    int rt=Build(1,N),cnt=0;
    for(int i=1;i<=N;i++){
        int x=a[i]+1;if(nxt[x])rt=Insert(rt,1,N,nxt[x]),add(fir[nxt[x]],-1),add(fir[nxt[x]]=i,1);
        else rt=Insert(rt,1,N,nxt[x]=++cnt),fir[nxt[x]]=i;
        while(ver[q[i].x]<cnt){ver[q[i].x]++;int y=idx[ver[q[i].x]];add(fir[x],-1),add(fir[x]=nxt[a[y]+1]=nxt[a[y]]+1,1);Insert(rt,1,N,nxt[a[y]+1]);}
        for(int k=idx[ver[q[i].x]];k<=i;k++)tr[nxt[a[k]+1]]=Insert(tr[nxt[a[k]]],1,N,k),add(fir[nxt[a[k]]],-1),add(fir[nxt[a[k]+1]],1);
        idx[ver[q[i].x]]=i;
        for(int j=1;j<=M&&Q[j].r==i;j++){
            int l=0,r=cnt,mn=X*Q[j].k;nxt[N+1]=0;
            while(l<=r){
                int mid=(l+r)>>1;
                if(Query(tr[nxt[mid]],1,N,Q[j].l,i)>=mn)r=mid-1;
                else l=mid+1;
            }
            ans[Q[j].id]=(l-1)*X+Q[j].l-1;
        }
    }
    for(int i=1;i<=M;i++)printf("%lld\n",ans[i]);
    return 0;
}

以上就是查找具有更新的至少X值的[L,R]范围内的最小索引的介绍和相关代码示例。