Rmq Problem

Rmq Problem

主席树 or 线段树

3339: Rmq Problem

题解:

一、主席树:

我们考虑建权值线段树,每个数字 x 保存它最后出现的位置

这样查询[l,r],就是找第r棵主席树中第一个值 < l 的

主席树上每个区间维护当前数中,权值从 l 到 r 中最后一次出现最靠左的位置

就是相当于维护区间最小值即可

Code:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 200000*20;
const int MX_A = 200001;

int n,q;
int lch[N],rch[N],mn[N],root[N],sz;

void clone(int t,int p){
    lch[t]=lch[p]; rch[t]=rch[p]; mn[t]=mn[p];
}

void insert(int &t,int p,int l,int r,int x,int d){
    t=++sz; clone(t,p);
    if(l!=r){
        int mid=(l+r)>>1;
        if(x<=mid) insert(lch[t],lch[p],l,mid,x,d);
        else insert(rch[t],rch[p],mid+1,r,x,d);
        mn[t]=min(mn[lch[t]],mn[rch[t]]);
    }
    else{ mn[t]=d; }
}

int query(int t,int l,int r,int ql){
    if(l==r) return l;
    int mid=(l+r)>>1;
    if(mn[lch[t]]<ql) return query(lch[t],l,mid,ql);
    else return query(rch[t],mid+1,r,ql);
}

int main(){
    freopen("a.in","r",stdin);
    scanf("%d%d",&n,&q);
    int a;
    for(int i=1;i<=n;i++){
        scanf("%d",&a); a++;
        insert(root[i],root[i-1],1,MX_A,a,i);
    }
    int l,r;
    while(q--){
        scanf("%d%d",&l,&r);
        printf("%d\n",query(root[r],1,MX_A,l)-1);
    }
}

二、线段树:

(抄的黄学长的)

首先按照左端点将询问排序

然后一般可以这样考虑

首先如何得到1-i的sg值呢

这个可以一开始扫一遍完成

接着考虑l-r和l+1-r的答案有何不同

显然是l-next[l]-1这一段所有sg值大于a[l]的变为a[l]

这一步如果暴力修改的话只有30分

但是修改区间我们可以想到线段树,这样就能a了

Code:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define D(x) cout<<#x<<" = "<<x<<"  "
#define E cout<<endl
const int INF = 0x3f3f3f3f;
const int NO_TAG = INF;
const int N = 200005;
using namespace std;

int sg[N],mark[N],n,m,a[N],next[N],last[N];

struct Data{
    int l,r,id,ans;
} q[N];
bool cmp_l(const Data &a, const Data &b){
    return a.l < b.l;
}
bool cmp_id(const Data &a, const Data &b){
    return a.id < b.id;
}

struct Node{
    int l,r,tag;
} pool[N*4];

void pushdown(int x){
    Node &t=pool[x];
    if(t.tag!=NO_TAG){
        pool[x*2].tag=min(pool[x*2].tag,t.tag);
        pool[x*2+1].tag=min(pool[x*2+1].tag,t.tag);
        t.tag=NO_TAG;
    }
}

void build(int x,int l,int r){
    Node &t=pool[x]; 
    t.l=l; t.r=r; t.tag=NO_TAG;
    if(l!=r){
        int mid=(t.l+t.r)>>1;
        build(x*2,l,mid); build(x*2+1,mid+1,r);
    }
    else{ t.tag=sg[l]; }
}

void setMin(int x,int ql,int qr,int d){
    Node &t=pool[x];
    if(ql<=t.l && t.r<=qr){ t.tag=min(t.tag,d); }
    else{
        int mid=(t.l+t.r)>>1;
        if(ql<=mid) setMin(x*2,ql,qr,d);
        if(qr>mid) setMin(x*2+1,ql,qr,d);
    }
}

int query(int x,int p){
//  D(x); D(p);
    Node &t=pool[x];
    if(t.l==t.r) return t.tag;
    else{
        pushdown(x);
        int mid=(t.l+t.r)>>1;
        if(p<=mid) return query(x*2,p);
        else return query(x*2+1,p);
    }
}

int main(){
    freopen("a.in","r",stdin);
    scanf("%d%d",&n,&m);
    int mex=0;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        mark[a[i]]++;
        if(a[i]==mex){ while(mark[mex]) mex++; }
        sg[i]=mex;
//      D(sg[i]); E;
    }
    build(1,1,n);
    for(int i=n;i>=1;i--){
        next[i]=last[a[i]]==0?n+1:last[a[i]]; last[a[i]]=i;
//      D(next[i]); E;
    }
    for(int i=1;i<=m;i++){
        scanf("%d%d",&q[i].l,&q[i].r);
        q[i].id=i;
    }
    sort(q+1,q+1+m,cmp_l);
    int now=1;
    for(int i=1;i<=m;i++){
        while(now<q[i].l){
            setMin(1,now,next[now]-1,a[now]);
            now++;
        }
        q[i].ans=query(1,q[i].r);
//      D(q[i].l); D(q[i].r); D(q[i].ans); E;
    }
    sort(q+1,q+1+m,cmp_id);
    for(int i=1;i<=m;i++){
        printf("%d\n",q[i].ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值