bzoj 3339 Rmq problem 离线+线段树

首先可以在O(n)的时间内求出1~i(for i 1~n)的答案ans[ i ]。然后对于l~r的答案与l+1~r答案的关系,把i~nxt[ i ]-1的所有大于a[i]的ans都改为a[i]就可以了,nxt[ i ]表示下一个最近的 值也是a[i]的位置。询问排一下序。

#include<cstdio>
#include<iostream>
#include<algorithm>
#define maxn 200005
using namespace std;
void read(int &a)
{
    a=0;char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9')
    {
        a*=10;a+=c-'0';
        c=getchar();
    }
}
struct Que{
    int l,r;
    int id,ans;
}q[maxn];
bool cmp(Que A,Que B)
{return A.l<B.l;}
bool cmp2(Que A,Que B)
{return A.id<B.id;}
int a[maxn];
bool use[maxn];
int ans[maxn];
struct XDS{
    struct xds
    {
        int l,r;
        int add;
    }tree[maxn<<2];
    void build(int x,int l,int r)
    {
        tree[x].l=l;
        tree[x].r=r;
        tree[x].add=maxn;
        if(l==r) return ;
        int mid=(l+r)>>1;
        build(x<<1,l,mid);
        build(x<<1|1,mid+1,r);
    }
    void spread(int x)
    {
        tree[x<<1].add=min(tree[x].add,tree[x<<1].add);
        tree[x<<1|1].add=min(tree[x].add,tree[x<<1|1].add);
    }
    int L,R;
    void change(int x,int d)
    {
        if(tree[x].l>=L&&tree[x].r<=R)
        {
            tree[x].add=min(tree[x].add,d);
            return ;
        }
        spread(x);
        int mid=(tree[x].l+tree[x].r)>>1;
        if(mid>=L) change(x<<1,d);
        if(mid<R)  change(x<<1|1,d);
    }
    int ask(int x)
    {
        if(tree[x].l==L&&tree[x].r==R)
            return min(ans[L],tree[x].add);
        spread(x);
        int mid=(tree[x].l+tree[x].r)>>1;
        if(mid>=L) return ask(x<<1);
        return ask(x<<1|1);
    }
}TR;
int fst[maxn],nxt[maxn];
int main()
{
    int n,m,Max=0;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        read(a[i]);
        Max=max(Max,a[i]);
    }
    for(int i=0;i<=Max;i++)
        fst[i]=n+1;
    for(int i=n;i>=1;i--)
    {
        nxt[i]=fst[a[i]];
        fst[a[i]]=i;
    }
    for(int i=1;i<=n;i++)
    {
        use[a[i]]=1;
        int A=ans[i-1];
        while(use[A]) A++;
        ans[i]=A;
    }
    for(int i=1;i<=m;i++)
    {
        read(q[i].l);
        read(q[i].r);
        q[i].id=i;
    }
    sort(q+1,q+m+1,cmp);
    TR.build(1,1,n);
    int H=1;
    for(int i=1;i<=n;i++)
    {
        while(q[H].l==i)
        {
            TR.R=TR.L=q[H].r;
            q[H].ans=TR.ask(1);
            H++;
        }
        TR.L=i,TR.R=nxt[i]-1;
        TR.change(1,a[i]);
    }
    sort(q+1,q+m+1,cmp2);
    for(int i=1;i<=m;i++)
        printf("%d\n",q[i].ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值