1. 程式人生 > 實用技巧 >luogu 4137 Rmq Problem / mex

luogu 4137 Rmq Problem / mex

題意:
n個數,m次詢問,每次詢問一個區間的mex(不在區間內的最小自然數)。
思路:
離線+線段樹
經典離線資料結構思維題
對詢問離線處理,按左端點分類。
從左到右列舉左端點,需要維護出以當前列舉下標i為左端點的時候,每個點[i..n]作為右端點的答案,這樣查詢的時候,只需要查詢對應右端點位置的值即可。
關鍵在於左端點移動時對答案帶來的改變。
當左端點向右移動一個單位時,考慮對哪些區間的答案產生影響。
注意到如果當前值為a[i],a[i]下一次出現的位置為nxt[i],那麼對[nxt[i]..n]範圍內的點,答案不會產生變化,因為實際區間內出現的元素沒有發生改變。
因此實際發生改變的區間為[i..nxt[i]-1]。
那麼接下來分析對這些區間帶來什麼樣的變化。
變化在於,沒有了a[i]這個元素,那麼如果原先的值小於等於a[i],由mex的定義,答案不發生改變;如果原先的答案大於a[i],那麼此時a[i]成為了最新的沒在區間內出現的最小的數了。
因此這些位置的答案都需要變成a[i]。
也就是說,我們需要做的事情其實是,維護一個數組,然後對某個區間取min,然後查詢單點的值。
那麼用線段樹來做這件事再合適不過。
一開始腦殘傻逼了以為是吉司機線段樹,後來意識到了因為沒有求區間和,所以不需要用吉司機那一套。
直接對每個區間打個lazy標記即可。
維護區間min的地方寫殘了WA了兩發。
這套做法能夠起作用的關鍵在於,隨著左端點的單位移動,陣列維護的答案變化是可以用資料結構很好地維護出來的。
是一個非常重要的思路。
列舉左端點,維護一個數組,記錄從當前左端點到每個右端點的答案,再考慮左端點移動對答案帶來的影響。

#include<bits/stdc++.h>
#define lson(p) p<<1
#define rson(p) p<<1|1
using namespace std;
const int maxn=2e5+10;
const int INF=0x3f3f3f3f;
int a[maxn],c[maxn],nxt[maxn];
int n,m,l,r;
int ans[maxn];
vector<pair<int,int> > q[maxn];
map<int,bool> mp;
map<int,int> last;
struct node { int mn; int lazy; void update(int v) { mn=min(v,mn); lazy=min(lazy,v); } }b[4*maxn]; void pushup(int p) { b[p].mn=min(b[lson(p)].mn,b[rson(p)].mn); } void build(int p,int l,int r) { b[p].lazy=INF; if (l==r) { b[p].mn=c[l];
return ; } int mid=(l+r)>>1; build(lson(p),l,mid); build(rson(p),mid+1,r); pushup(p); } void pushdown(int p) { if (b[p].lazy!=INF) { b[lson(p)].update(b[p].lazy); b[rson(p)].update(b[p].lazy); b[p].lazy=INF; } } void update(int p,int l,int r,int L,int R,int val) { if (L<=l&&r<=R) { b[p].mn=min(b[p].mn,val); b[p].lazy=min(b[p].lazy,val); return ; } pushdown(p); int mid=(l+r)>>1; if (L<=mid) update(lson(p),l,mid,L,R,val); if (R>mid) update(rson(p),mid+1,r,L,R,val); pushup(p); } int query(int p,int l,int r,int pos) { if (l==r) return b[p].mn; int mid=(l+r)>>1; pushdown(p); if (pos<=mid) return query(lson(p),l,mid,pos); else return query(rson(p),mid+1,r,pos); } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%d",&a[i]); for (int i=1;i<=n;i++) nxt[i]=-1; for (int i=1;i<=n;i++) { if (last.count(a[i])) { nxt[last[a[i]]]=i; } last[a[i]]=i; } for (int i=1;i<=m;i++) { scanf("%d%d",&l,&r); q[l].push_back(make_pair(r,i)); } for (int i=1;i<=n;i++) { mp[a[i]]=true; int tem=c[i-1]; while (mp[tem]) tem++; c[i]=tem; } build(1,1,n); for (int i=1;i<=n;i++) { for (int j=0;j<(int)q[i].size();j++) { int x=q[i][j].first,y=q[i][j].second; ans[y]=query(1,1,n,x); } if (nxt[i]!=-1) update(1,1,n,i,nxt[i]-1,a[i]); else update(1,1,n,i,n,a[i]); } for (int i=1;i<=m;i++) printf("%d\n",ans[i]); return 0; }