• 「UOJ749」稳健型选手


    题目

    点这里看题目。

    分析

    首先考虑 \(q=1,l=1,r=n\) 的情况。我们设 QAQ 取了 \(k\) 张牌,牌的编号分别为 \(b_1,b_2,\dots,b_k\),不难发现判定合法性的直接条件:

    一种选法 \(b_1,b_2,\dots,b_k\) 是合法的,当且仅当 \(\forall 1\le j\le k,b_j\ge 2j-1\)

    这样,\(k\) 的最大值就是 \(\left\lceil\frac{n}{2}\right\rceil\),记这个值为 \(m\)。我们将上述条件等价转化一下,其实可以得到一个更好用的条件:

    一种选法 \(b_1,b_2,\dots,b_k\) 是合法的,当且仅当 \(\forall 1\le j\le k\),区间 \([2j-1,n]\) 中被选中的牌 \(\le n-j\)

    这可以直接导出一种 \(O(n\log n)\) 的做法:维护备选集合 \(S\)。我们按照 \(j\) 从大到小的顺序依次考虑每个区间 \([2j-1,n]\),每次将相较于 \([2j+1,n]\) 新增的牌加入到 \(S\) 中,而后取出 \(S\) 中权值最大的牌、从 \(S\) 中删除并将它的权值算入答案。

    Remark.

    顺便一说,这个问题实际上就是「NOI2017」蔬菜的弱化。所以堆贪心的思路是共通的。

    回到区间询问上来。首先,我们可以假设 \(l,r\) 的奇偶性不同,如果相同的话 \(r\) 这张牌一定可以取到。则上述贪心过程可以很容易地从 \([l,r]\) 扩展到 \([l-2,r]\),如果我们还能实现从 \([l,r]\)\([l,r+2]\) 的扩展,那么我们就可以实现一个回滚莫队。

    考虑这样修改的影响:我们一开始会先处理 \(r+1,r+2\) 这两张牌,将权值较大的一张记入答案,并将权值较小的一张留在 \(S\) 里面,然后进行 \([l,r]\) 的贪心。如果我们得到了 \([l,r]\) 贪心选择的牌集合 \(B\),那么加入了一张牌对于 \(B\) 的影响,实际上就是先将牌加入到 \(B\) 当中,再将 \(B\) 中最小的一张牌删掉——因为在最终结果中,\(B\) 中的牌和新加入的牌都是可以任意保留的。

    Remark.

    考场上,我似乎先入为主地以为扩展到 \([l,r+2]\) 的过程是不可实现的 。

    “一次做一步扩展”可以很容易地修改成“一次做多步扩展”。直接利用上述思想,如果我们想要从 \([l,r]\) 扩展到 \([l,r+2k]\),那么我们可以先取出 \([r+1,r+2],[r+3,r+4],\dots,[r+2k-1,r+2k]\) 各个区间的较大值记入答案,将各个区间的较小值和 \([l,r]\) 贪心的结果 \(B\) 合并——也即选取这些牌中的前 \(\frac{r-l+1}{2}\) 大——就得到了答案。

    怎么用好这样的操作?“一次做多步扩展”其实就是说“区间信息可以二合一”,可以二合一的区间信息处理可以使用分治。模仿上面的合并即可,查询前 \(\frac{r-l+1}{2}\) 大可以使用主席树。复杂度即为 \(O(n\log^2n+q\log n)\)

    代码

    #include <queue>
    #include <cstdio>
    #include <vector>
    #include <utility>
    #include <algorithm>
    
    #define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
    #define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )
    
    typedef long long LL;
    
    const int MAXN = 2e5 + 5, MAXS = 5e6 + 5;
    
    template<typename _T>
    inline void Read( _T &x ) {
    	x = 0; char s = getchar(); bool f = false;
    	while( ! ( '0' <= s && s <= '9' ) ) { f = s == '-', s = getchar(); }
    	while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s ^ '0' ), s = getchar(); }
    	if( f ) x = -x;
    }
    
    template<typename _T>
    inline void Write( _T x ) {
    	if( x < 0 ) putchar( '-' ), x = -x;
    	if( 9 < x ) Write( x / 10 );
    	putchar( x % 10 + '0' );
    }
    
    template<typename _T>
    inline _T Min( const _T &a, const _T &b ) {
    	return a < b ? a : b;
    }
    
    template<typename _T>
    inline _T Max( const _T &a, const _T &b ) {
    	return a > b ? a : b;
    }
    
    std :: vector<int> qry[MAXN], beg[2];
    
    int qL[MAXN], qR[MAXN];
    LL ans[MAXN];
    
    std :: priority_queue<int, std :: vector<int>, std :: less<int> > lRt;
    std :: priority_queue<int, std :: vector<int>, std :: greater<int> > sRt;
    
    LL pref[MAXN];
    int siz[MAXS]; LL su[MAXS];
    int lch[MAXS], rch[MAXS];
    int rt[MAXN], ntot;
    
    int A[MAXN];
    int val[MAXN], tot;
    
    int N, Q;
    
    inline void Discrete() {
    	rep( i, 1, N ) val[++ tot] = A[i];
    	std :: sort( val + 1, val + 1 + tot );
    	tot = std :: unique( val + 1, val + 1 + tot ) - val - 1;
    	rep( i, 1, N ) A[i] = std :: lower_bound( val + 1, val + 1 + tot, A[i] ) - val;
    }
    
    inline int NewNode() {
    	static int r;
    
    	r = ++ ntot;
    	lch[r] = rch[r] = siz[r] = su[r] = 0;
    	return r;
    }
    
    inline void Copy( const int &a, const int &b ) {
    	lch[a] = lch[b], rch[a] = rch[b], siz[a] = siz[b], su[a] = su[b];
    }
    
    inline void Upt( const int &x ) {
    	su[x] = su[lch[x]] + su[rch[x]];
    	siz[x] = siz[lch[x]] + siz[rch[x]];
    }
    
    int ConsUpdate( const int &x, const int &l, const int &r, const int &p, const int &delt ) {
    	int cur = NewNode(); Copy( cur, x );
    	if( l == r ) siz[cur] += delt, su[cur] += val[p] * delt;
    	else {
    		int mid = ( l + r ) >> 1;
    		if( p <= mid ) lch[cur] = ConsUpdate( lch[x], l, mid, p, delt );
    		else rch[cur] = ConsUpdate( rch[x], mid + 1, r, p, delt );
    		Upt( cur );
    	}
    	return cur;
    }
    
    void DynaUpdate( int &x, const int &l, const int &r, const int &p, const int &delt ) {
    	x = x ? x : NewNode();
    	if( l == r ) { siz[x] += delt, su[x] += delt * val[p]; return ; }
    	int mid = ( l + r ) >> 1;
    	if( p <= mid ) DynaUpdate( lch[x], l, mid, p, delt );
    	else DynaUpdate( rch[x], mid + 1, r, p, delt );
    	Upt( x ); 
    }
    
    LL Search( const int &x, const int &y, const int &l, const int &r, const int &rnk ) {
    	if( ! x && ! y ) return 0;
    	if( l == r ) return 1ll * rnk * val[l];
    	int mid = ( l + r ) >> 1;
    	if( rnk <= siz[rch[x]] + siz[rch[y]] )
    		return Search( rch[x], rch[y], mid + 1, r, rnk );
    	return Search( lch[x], lch[y], l, mid, rnk - siz[rch[x]] - siz[rch[y]] ) + su[rch[x]] + su[rch[y]];
    }
    
    void Divide( const int &l, const int &r, const std :: vector<int> &rng ) {
    	if( r <= l + 1 || rng.empty() ) return ;
    	std :: vector<int> lef, rig, crs;
    	int mid = ( ( l - 1 ) / 2 + ( r - 1 ) / 2 + 2 ) / 2 * 2 - ( r & 1 ), n = rng.size();
    	for( int i = 0 ; i < n ; i ++ ) {
    		int x = rng[i];
    		if( qL[x] <= mid && mid < qR[x] ) crs.push_back( x );
    		else {
    			if( qR[x] <= mid ) lef.push_back( x );
    			if( mid  < qL[x] ) rig.push_back( x );
    		}
    	}
    	Divide( l, mid, lef );
    	Divide( mid + 1, r, rig );
    	n = crs.size();
    	for( int i = l ; i <= mid ; i += 2 ) qry[i].clear();
    	for( int i = 0 ; i < n ; i ++ ) qry[qL[crs[i]]].push_back( crs[i] );
    	ntot = 0; 
    	while( ! sRt.empty() ) sRt.pop();
    	for( int i = mid + 1 ; i <= r ; i += 2 ) {
    		rt[i + 1] = i == mid + 1 ? 0 : rt[i - 1];
    		pref[i + 1] = i == mid + 1 ? 0 : pref[i - 1];
    		sRt.push( A[i] ), pref[i + 1] += val[A[i]]; 
    		sRt.push( A[i + 1] ), pref[i + 1] += val[A[i + 1]];
    		rt[i + 1] = ConsUpdate( rt[i + 1], 1, tot, sRt.top(), +1 );
    		pref[i + 1] -= val[sRt.top()], sRt.pop();
    	}
    	int curRt = 0;
    	while( ! lRt.empty() ) lRt.pop();
    	for( int i = mid - 1 ; i >= l ; i -= 2 ) {
    		lRt.push( A[i] ), lRt.push( A[i + 1] );
    		int h = lRt.top(); lRt.pop();
    		DynaUpdate( curRt, 1, tot, h, +1 );
    		int m = qry[i].size();
    		for( int j = 0 ; j < m ; j ++ ) {
    			int x = qry[i][j];
    			ans[x] += Search( curRt, rt[qR[x]], 1, tot, ( mid - qL[x] + 1 ) >> 1 ) + pref[qR[x]];
    		}
    	}
    }
    
    int main() {
    	Read( N ), Read( Q );
    	rep( i, 1, N ) Read( A[i] );
    	Discrete();
    	rep( i, 1, Q ) {
    		Read( qL[i] ), Read( qR[i] );
    		if( ( qR[i] - qL[i] + 1 ) & 1 )
    			ans[i] += val[A[qR[i] --]];
    		if( qL[i] <= qR[i] ) {
    			if( qR[i] == qL[i] + 1 )
    				ans[i] += Max( val[A[qL[i]]], val[A[qR[i]]] );
    			else
    				beg[qL[i] % 2].push_back( i );
    		}
    	}
    	Divide( 1, N - ( N & 1 ), beg[1] );
    	Divide( 2, N - 1 + ( N & 1 ), beg[0] );
    	rep( i, 1, Q ) Write( ans[i] ), putchar( '\n' );
    	return 0;
    }
    
  • 相关阅读:
    “Installation error: INSTALL_PARSE_FAILED_MANIFEST_MALFORMED”
    【问底】夏俊:深入站点服务端技术(一)——站点并发的问题
    java反射调用方法
    linux 文件操作系统调用
    64位win7中使用vs2013为python3.4安装pycrypto-2.6.1插件报Unable to find vcvarsall.bat异常解决方式
    ViewPager+RadioGroup实现标题栏切换,Fragment切换
    Android View系统解析(下)
    高速学会Mac上托管代码到github(具体解释)
    linux程序设计——多线程(第十二章)
    使用工作流更新子记录
  • 原文地址:https://www.cnblogs.com/crashed/p/16572478.html
Copyright © 2020-2023  润新知