Luogu4137 Rmq Problem / mex(主席树,线段树上二分)(区间mex问题)

https://www.luogu.com.cn/problem/P4137?contestId=68990
image

  • 若知道所有数字最后出现的位置,对于查询区间[l, r], num最后出现的位置pos小于l的话,则[l,r]一定不含num,pos大于r的话,就不知道了。
  • 使用主席树,对于区间[l,r]查询rt[r],则可以避免pos大于r的情况
  • 维护权值线段树,值是权值最后出现的位置。
  • 样答案就可以在rt[r]根的树上二分,找位小于l的最小的数
  • 注意主席树值域是题目值域+1
    同样主席树维护数值最后出现位置的还有 区间种类数问题
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false) ,cin.tie(0), cout.tie(0);
//#pragma GCC optimize(3,"Ofast","inline")
#define ll long long
//#define int long long
const int N = 2e5 + 5;
const int M = 1e6 + 5;
const int INF = 0x3f3f3f3f;
const ll LNF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1004535809;
const double PI = acos(-1.0);
int a[N], rt[N], sz;
struct Tree {
   int val, lc, rc;
} tree[N * 40];
void modify( int pos, int val, int l, int r, int &now, int pre) {
   now = ++ sz;
   tree[now] = tree[pre];
   if( l == r) {
       tree[now].val = val; return;
   }
   int mid = l + r >> 1;
   if(pos <= mid) modify( pos, val, l, mid, tree[now].lc, tree[pre].lc);
   else modify( pos, val, mid + 1, r, tree[now].rc, tree[pre].rc);
   tree[now].val = min(tree[tree[now].lc].val, tree[tree[now].rc].val);
}
int query( int val, int l, int r, int now ) {

   if(l == r) {
       return l;
   }

   int mid = l + r >> 1;
   if(tree[tree[now].lc].val < val) return query ( val, l, mid, tree[now].lc);
   else return query( val, mid + 1, r, tree[now].rc );
}
int main() {
   IOS
   int n, m; cin >> n >> m;
   int limit = N - 5 + 1;
   for ( int i = 1; i <= n; ++ i ) cin >> a[i], modify( a[i], i, 0, limit, rt[i], rt[i - 1]);
   while( m -- ) {
       int l, r; cin >> l >> r;
       cout << query( l, 0, limit, rt[r]) << '\n';
   }
   return 0;
}
posted @ 2022-05-05 21:57  qingyanng  阅读(27)  评论(0编辑  收藏  举报