zoukankan      html  css  js  c++  java
  • 题解 P4137 【Rmq Problem / mex】

    一种不太难想的做法?但可能码量大一些???

    先把所有的询问按照 (l) 排序。

    现在考虑处理出一颗线段树维护一个区间,其中某一个叶子节点表示的含义,为:当前 (l)((l quad-quad n ))这一区间内未出现的自然数是谁。

    那么最初是的 (l)(1),即每个叶子节点都表示从 (1) 号点到 (x) 点这一段区间内未出现的最小自然数。

    那么我们要先处理出每个位置到 (1) 号位置这一区间的最小未出现的自然数。

    这个处理可以通过维护最小出现次数的权值线段树来做。

    然后将处理出来的结果存入一个线段树内。

    考虑对于 $l = 1 $ 的点,其答案直接通过这个线段树来作查询即可。

    因为按照 (l) 排序了,所以接下来的我们需要去更新线段树的其他节点。

    然后考虑让 (l) 向左移动,我们可以发现,删去一个数 (a[l]) 其实就是让后面的点对其取 (min)。所以可以维护一个取 (min) 标记。

    但是考虑到某个元素可能重复出现,所以可以再开一个 (vector) 维护每个点出现的位置,然后取 (min) 操作就是对此点从当前位置到其下一个出现的位置这一段区间对 (x)(min)

    具体实现上因为在 (vector) 上二分找位置不太现实,然而我们已经按照 (l)排序,每个 (vector) 内存的位置单调递增,所以可以维护一个 (fir[x]) 表示当前的这个权值所对应的 (vector) 到了那个点,每次更新完后(++ fir)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    int read() {
    	char cc = getchar(); int cn = 0, flus = 1;
    	while(cc < '0' || cc > '9') {  if( cc == '-' ) flus = -flus;  cc = getchar();  }
    	while(cc >= '0' && cc <= '9')  cn = cn * 10 + cc - '0', cc = getchar();
    	return cn * flus;
    }
    const int N = 2e5 + 5;
    #define rep( i, s, t ) for( register int i = s; i <= t; ++ i )
    #define ls(x) ( x * 2 )
    #define rs(x) ( x * 2 + 1 )
    #define Size 
    #define inf 123456789
    struct Tree { int l, r, mi, mark; }tr[N * 4], t[N * 4];
    struct Q { int l, r, id, ans; }q[N];
    vector< int> vec[N];
    int a[N], n, m, fir[N], cnt;
    bool cmp( Q x, Q y ) {
    	return ( x.l == y.l ) ? x.r < y.r : x.l < y.l ;
    } 
    bool cmp2( Q x, Q y ) {
    	return x.id < y.id;
    }
    // 权值线段树 
    void insert1( int x, int l, int r, int wh ) {
    	if( l == r ) { ++ tr[x].mi; return ; }; 
    	int mid = ( l + r ) >> 1;
    	if( mid >= wh ) insert1( ls(x), l, mid, wh );
    	else insert1( rs(x), mid + 1, r, wh );
    	tr[x].mi = min( tr[ls(x)].mi, tr[rs(x)].mi ); //用权值线段树维护区间最小出现次数 
    }
    int Query1( int x, int l, int r ) {
    	if( l == r ) return l;
    	int mid = ( l + r ) >> 1;
    	if( !tr[ls(x)].mi ) return Query1( ls(x), l, mid ); // 如果左儿子内最小出现次数为0,往左儿子走 
    	else return Query1( rs(x), mid + 1, r ); // 否则往右儿子走 
    }
    // 线段树 
    void pushmark( int x ) { //区间取min的下传 
    	if( t[x].mark != inf ) {
    		t[ls(x)].mark = min( t[ls(x)].mark, t[x].mark );
    		t[ls(x)].mi = min( t[ls(x)].mi, t[ls(x)].mark );
    		t[rs(x)].mark = min( t[rs(x)].mark, t[x].mark );
    		t[rs(x)].mi = min( t[rs(x)].mi, t[rs(x)].mark ); 
    	}
    }
    void Ins( int x, int l, int r, int wh, int val ) {
    	t[x].mark = inf ; //注意mark初始化为inf  
    	if( l == r ) { t[x].mi = val; return ;}
    	int mid = ( l + r ) >> 1 ;
    	if( mid >= wh ) Ins( ls(x), l, mid, wh, val );
    	else Ins( rs(x), mid + 1, r, wh, val );
    	t[x].mi = min( t[ls(x)].mi, t[rs(x)].mi ); // 线段树同样维护区间最小值,方便取min操作 
    }
    
    void Cover( int x, int l, int r, int ll, int rr, int val ) {
    	if( ll <= l && r <= rr ) { 
    		t[x].mark = min( t[x].mark, val ) ; //取min 
    		t[x].mi = min( t[x].mi, t[x].mark ) ;
    		return ; 
    	}
    	if( l > rr || r < ll ) return ; //区间越界 
    	pushmark(x) ; int mid = ( l + r ) >> 1 ; 
    	Cover( ls(x), l, mid, ll, rr, val ) ;
    	Cover( rs(x), mid + 1, r, ll, rr, val ) ;
    	return ;
    }
    
    int Query2( int x, int l, int r, int wh ) { // 询问位置为wh的点的答案 
    	if( l == r ) return t[x].mi; 
    	pushmark(x); int mid = ( l + r ) >> 1;
    	if( mid >= wh ) return Query2( ls(x), l, mid, wh ); 
    	else return Query2( rs(x), mid + 1, r, wh );
    }
    
    void input() {
    	n = read(), m = read();
    	rep( i, 1, n ) {
    		a[i] = read() + 1;  //加1,方便线段树处理 
    		if( a[i] > n ) continue; // 如果a[i] 大于 n 就没有必要加入  
    		vec[a[i]].push_back(i);
    	}
    	rep( i, 1, n ) vec[i].push_back(n + 1); //权值至多有n种,在每个点的vector最后面插入一个元素(n + 1)。 
    }
    void solve() {
    	int x, ll = 1;
    	rep( i, 1, n ) {
    		if( a[i] <= n ) insert1( 1, 1, n, a[i] ); //如果比n小才插入权值线段树 
    		x = Query1( 1, 1, n ), Ins( 1, 1, n, i, x );
    		// x为询问 1-n 中未出现的权值,然后将作为位置i的答案插入线段树。 
    	}
    	rep( i, 1, m ) q[i].l = read(), q[i].r = read(), q[i].id = i;
    	sort( q + 1, q + m + 1, cmp );
    	rep( i, 1, m ) {
    		while( ll < q[i].l ) {
    			x = a[ll];
    			if( x <= n ) Cover( 1, 1, n, ll + 1, vec[x][fir[x] + 1] - 1, x ), ++ fir[x]; 
    			++ ll;
    		}
    		q[i].ans = Query2( 1, 1, n, q[i].r ) - 1;
    	} 
    } 
    signed main()
    {
    	input() , solve() ;
    	sort( q + 1, q + m + 1, cmp2 ); 
    	rep( i, 1, m ) printf("%d
    ", q[i].ans );
    	return 0;
    }
    
  • 相关阅读:
    Freewriting23_Hunting job again.
    PYDay4基本数据类型、字符串、元组、列表、字典
    PYDay3初识python
    PYDay2linux基础\常用命令
    PYDay1洗剑
    ASP.NET 2.0中GridView无限层复杂表头的实现(datagrid大同小易,repeater就不必这样,直接有头模版布局就可以)
    JavaScript获取HTML DOM节点元素的方法的总结
    Asp.net中实现同一用户名不能同时登陆(转)
    在JS文件中写脚本几种常见的形式
    获取repeater中头模版中的控件时要加从controls[0]
  • 原文地址:https://www.cnblogs.com/Soulist/p/10687490.html
Copyright © 2011-2022 走看看