题目
点这里看题目。
分析
首先考虑 \(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;
}