1. 程式人生 > >區間MEX 線段樹維護mex陣列

區間MEX 線段樹維護mex陣列

NKOJ 4254 區間MEX

問題描述

給你一個長度為n的數列,元素編號1到n,第i個元素值為Ai。現在有m個形如(L,R)的提問,你需要回答出區間[L,R]的mex值。即求出區間[L,R]中沒有出現過的最小的非負整數。

輸入格式

第一行,兩個整數n和m
第二行,n個空格間隔的整數,表示數列A
接下來m行,每行兩個整數L,R,表示一次詢問

輸出格式

m行,每行一個整數,表示對應詢問的答案。

樣例輸入

7 5
0 2 1 0 1 3 2
1 3
2 3
1 4
3 6
2 7

樣例輸出

3
0
3
2
4

資料範圍

1<=n,m<=200000
0<=Ai<=200000
1<=L<=R<=n

分析:

由於沒有修改操作,可以考慮離線演算法。

首先考慮暴力的做法。對於固定的左端點,如何用O(n)的方法求出左端點與右端點形成的mex陣列的值?用一個mark陣列從下到大依次列舉沒有出現過的數值即可。考慮到mex陣列單調不減均攤時間複雜度O(n)。如果詢問區間的左端點只有一個,那麼就能像這樣O(n)解決。

然而本題的左端點是任意的。但是根據離線演算法的基本操作,我們可以把討論有序化。不妨將詢問按左端點從小到大排序。現在假設我們已經求出了以i為左端點的所有區間的mex值,想要得到以i+1為左端點的所有區間的mex值,考慮兩種狀態如何轉移。

從i到i+1,只有一個不同:那就是有些區間裡本來有Ai但現在沒有了。顯然只有這樣的區間才會受到一定影響。“這樣的區間”是哪些區間呢?在以i+1為左端點的區間中,這樣的區間是右端點在下一個Ai出現前的區間,用一個連結串列可以方便地維護。

這些區間會受到怎樣的影響?考慮Aimexj的大小關係。如果mexjAi要大,那麼把mexj更新為Ai;否則Ai就不會對mexj的值產生影響。這樣做問題就解決了。處理區間修改問題,使用線段樹。

最後總結一下做法:

1預處理:

(1)用O(n)將以1為左端點的mex值預處理出來;
(2)預處理出每個數下一次出現的位置;
(3)按區間左端點將詢問排序;

2.從1到n由小到大討論。處理完以i為左端點的詢問後,對mex陣列進行區間修改。

程式碼:

#include<stdio.h>
#include<algorithm>
#include<cstring>
#define MAXM 200005
#define MAXN 200005
#define MAXT 800005
using namespace std;

int N,M,A[MAXN],mex[MAXN],Ans[MAXM],nex[MAXN],las[MAXN];
bool mark[MAXN];
struct node{int l,r,id;}qry[MAXM];

bool operator<(node x,node y)
{
    return x.l<y.l;
}

int tot,lazy[MAXT],ls[MAXT],rs[MAXT],a[MAXT],b[MAXT],v[MAXT];
//lazy等於-1,說明沒有需要下放的操作
void Putdown(int p)
{
    int Ls=ls[p],Rs=rs[p];
    if(~lazy[Ls])lazy[Ls]=min(lazy[p],lazy[Ls]);
    else lazy[Ls]=lazy[p];
    if(~lazy[Rs])lazy[Rs]=min(lazy[p],lazy[Rs]);
    else lazy[Rs]=lazy[p];
    v[Ls]=min(v[Ls],lazy[p]);
    v[Rs]=min(v[Rs],lazy[p]);
    lazy[p]=-1;
}

int Build(int x,int y)
{
    tot++;
    int mid=x+y>>1,p=tot;
    a[p]=x;b[p]=y;
    if(x==y)
    {
        v[p]=mex[x];
        return p;
    }

    ls[p]=Build(x,mid);
    rs[p]=Build(mid+1,y);
    v[p]=min(v[ls[p]],v[rs[p]]);

    return p;
}

void Modify(int p,int l,int r,int k)
{
    if(b[p]<l||a[p]>r)return;
    if(~lazy[p])Putdown(p);
    if(a[p]>=l&&b[p]<=r)
    {
        lazy[p]=k;
        v[p]=min(v[p],k);
        return;
    }

    int mid=a[p]+b[p]>>1;
    Modify(ls[p],l,r,k);
    Modify(rs[p],l,r,k);
}

int GetAns(int p,int k)
{
    if(~lazy[p])Putdown(p);
    if(a[p]==b[p]&&a[p]==k)return v[p];
    int mid=a[p]+b[p]>>1;
    if(k<=mid)return GetAns(ls[p],k);
    return GetAns(rs[p],k);
}

int main()
{
    int i,j=0;

    scanf("%d%d",&N,&M);
    for(i=1;i<=N;i++)scanf("%d",&A[i]);
    for(i=N;i;i--)
    {
        if(las[A[i]]==0)nex[i]=N+1;
        else nex[i]=las[A[i]];
        las[A[i]]=i;
    }
    for(i=1;i<=M;i++)scanf("%d%d",&qry[i].l,&qry[i].r),qry[i].id=i;

    for(i=1;i<=N;i++)
    {
        mark[A[i]]=true;
        for(;;j++)if(!mark[j])break;
        mex[i]=j;
    }

    sort(qry+1,qry+M+1);
    memset(lazy,-1,sizeof(lazy));
    Build(1,N);

    for(i=1,j=1;i<=N&&j<=M;i++)
    {
        while(qry[j].l==i)
        {
            Ans[qry[j].id]=GetAns(1,qry[j].r);
            j++;
        }
        Modify(1,1,nex[i]-1,A[i]);
    }

    for(i=1;i<=M;i++)printf("%d\n",Ans[i]);
}

相關推薦

區間MEX 線段維護mex陣列

NKOJ 4254 區間MEX 問題描述 給你一個長度為n的數列,元素編號1到n,第i個元素值為Ai。現在有m個形如(L,R)的提問,你需要回答出區間[L,R]的mex值。即求出區間[L,R]中沒有出現過的最小的非負整數。 輸入格式 第一行

[Noi2016]區間[離散化+線段維護+決策單調性]

fin include efi cmp http 說明 int min unique 4653: [Noi2016]區間 Time Limit: 60 Sec Memory Limit: 256 MBSubmit: 621 Solved: 329[Submit][

BZOJ.3585.mex(線段)

min href turn har www tdi markdown opera 區間 題目鏈接 考慮\([1,i]\)的\(mex[i]\),顯然是單調的 而對於\([l,r]\)與\([l+1,r]\),如果\(nxt[a[l]]>r\),那麽\([l+1,r]\

FJUT3568 中二病也要敲程式碼(線段維護區間連續最值)題解

題意:有一個環,有1~N編號,m次操作,將a位置的值改為b,問你這個環當前最小連續和多少(不能全取也不能不取) 思路:用線段樹維護一個區間最值連續和。我們設出兩個變數Lmin,Rmin,Mmin表示區間左邊最小連續和,右邊最小連續和,區間最小連續和,顯然這可以通過這個方式更新維護。 現在我們已經可以維

jsk Star War (線段維護區間最小最大值 + 二分)

Description 公元20XX年,人類與外星人之間的大戰終於爆發。 現有一個人類軍團,由n名士兵組成,第i個士兵的戰鬥力值對應一個非負整數ai (1 \leq i \leq n1≤i≤n)。 有一天,某個戰力爆表的外星人NaN單獨向地球人宣戰,已知它的戰力值為k (1 \leq

最敏捷的機器人(線段維護區間最值)

題面: Wind設計了很多機器人。但是它們都認為自己是最強的,於是,一場比賽開始了……機器人們都想知道誰是最敏捷的,於是它們進行了如下一個比賽。首先,他們面前會有一排共n個數,它們比賽看誰能最先把每連續k個數中最大和最小值寫下來,當然,這些機器人運算速度都很,它們比賽的是誰寫得快。但是Wind也想知道答案,

線段維護區間(平方和,立方和)修改區間(加,賦值,乘)

題目地址 /* * @Author: hannibal * @Date: 2018-08-07 10:42:26 * @Last Modified by: hannibal * @Last Modified time: 2018-08-07 17:08:

牛課練習賽34 Flittle w and Discretization 主席維護Mex

ittle w and Discretization 主席樹維護Mex。 每個右端點 r 維護出一棵 在[1, r ] 區間中 其他所有的 值離這個 r 最近的的位置是多少。 然後詢問區間[L,R]的時候,從rt[R] 出發,然後如果左兒子的中所有出線位置的最小值 >= L, 則說明他們所有的點都

Fast Arrangement (線段 維護區間最大值 lazy標記)

Fast Arrangement 題意: 有一列火車同一時刻只能承載K個人,然後有Q個人要買票(給出Q組區間表示要買票的區間),(注意: 火車行駛區間為a-b的範圍),問那個人可以成功買到票(先到先得)

HDU 5726 GCD (線段維護區間gcd)

GCD Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submi

線段維護區間最大連續和

在我的理解中,線段樹就是線段組成的樹,越靠近根部的層,線段的長度越長,一個結構體(樹的每一個點的左邊界,右邊界以及要維護的值)和三個函式(build——拆的過程),update(更新的過程,更新的時候要先二分找到最底層的區間,然後需要向上將所有的區間都更新了),

hdu1540(線段維護連續區間模型)

/* translation: 打地道戰,n個村莊用地道連成一條直線,鬼子有時破壞掉一個村莊,這時八路軍要修好這個據點。現在要求詢問任意一個村莊, 得出這個村莊現在與幾個村莊相連,包括它本身。 solution: 線段樹維護連續子區間 將村莊抽象成一個點,正常下值為

P4198 樓房重建 (線段維護區間上升子序列)

題目描述 小A的樓房外有一大片施工工地,工地上有N棟待建的樓房。每天,這片工地上的房子拆了又建、建了又拆。他經常無聊地看著窗外發呆,數自己能夠看到多少棟房子。 為了簡化問題,我們考慮這些事件發生在一個二維平面上。小A在平面上(0,0)點的位置,第i棟樓房可以用一條連線

[永不止步-2017]_區間第K大-線段維護

把線段樹的結點的資料域設定為vector型別即可別的操作為區間更新模板 思路就是這樣runtime error暫時沒改對 #include<bits/stdc++.h> using namespace std; #define inf 0x3f3f3f

[UOJ #222][NOI2016]區間線段

ont 線段樹 div 最短 ans oid tro lib read Description 在數軸上有 n個閉區間 [l1,r1],[l2,r2],...,[ln,rn]。現在要從中選出 m 個區間,使得這 m個區間共同包含至少一個位置。換句話說,就是使得存在一個 x

Codeforces Round #426 (Div. 2) D. The Bakery(線段維護dp)

src lap codeforce blank com date close scanf logs 題目鏈接: Codeforces Round #426 (Div. 2) D. The Bakery 題意: 給你n個數,劃分為k段,每段的價值為這一段不同的數的個數,問如何

bzoj 1798 雙標記區間修改線段

scrip namespace c++ define sample -c bit 變化 sea 1 #include<bits/stdc++.h> 2 using namespace std; 3 #define MAXN 100000 4 #defi

WHYZOJ-#53 線段區間修改(線段)

namespace 標記 upd build out state long 輸入 std 【題目描述】: 如題,已知一個數列,你需要進行下面兩種操作: 1.將某區間每一個數加上x 2.求出某區間每一個數的和 【輸入描述】: 第一行包含兩個整數N、M,分別表示該數列數字

hihoCoder 1078 區間查詢線段

模板 代碼 maintain 可能 mes () std 測試數據 int 題面: 對於小Ho表現出的對線段樹的理解,小Hi表示挺滿意的,但是滿意就夠了麽?於是小Hi將問題改了改,又出給了小Ho: 假設貨架上從左到右擺放了N種商品,並且依次標號為1到N,其中標號為i的商品的

HDU 6155 Subsequence Count 線段維護矩陣

input ans ons tom thml other family 卡常 子序列 Subsequence Count Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 256000/256000 K (J