Solution -「國家集訓隊」「洛谷 P2839」Middle
阿新 • • 發佈:2020-08-07
\(\mathcal{Description}\)
Link.
給定序列 \(\{a_n\}\),\(q\) 組詢問,給定 \(a<b<c<d\),求 \(l\le[a,b],r\le[c,d]\) 的子序列 \([l,r]\) 的中位數最大值。若長度為偶數,中位數取中間兩數較大的一個。強制線上。
\(n\le2\times10^4\),\(q\le2.5\times10^4\)。
\(\mathcal{Solution}\)
crashed:眾所周知,中位數是可以二分的。
考慮單組詢問,二分中位數 \(mid\),把序列中大於等於 \(mid\) 的值設為 \(1\)
多組詢問,發現對於每個 \(mid\),序列的長相都不盡相同。但序列的變化是極其有限的——當 \(mid\) 遞增,每個位置只會又 \(1\) 變為 \(0\) 一次。
由此可以想到主席樹。離散化之後,預處理 \(mid=1,2,\dots,n\) 時的序列,建成主席樹。樹上維護區間和,區間最大字首,區間最大字尾。處理詢問時仍二分 \(mid\),利用以 \(mid\) 為根的這棵權值線段樹的資訊,求出區間最大和。顯然最大和為 \([a,b)\text{最大字尾}+[b,c]\text{之和}+(c,d]\text{最大字首}\)
複雜度 \(\mathcal O(n\log^2n)\)。
\(\mathcal{Code}\)
#include <cstdio> #include <vector> #include <algorithm> typedef std::pair<int, int> pii; const int MAXN = 20000; int n, a[MAXN + 5], tval[MAXN + 5], root[MAXN + 5]; std::vector<int> apr[MAXN + 5]; inline int rint () { int x = 0; char s = getchar (); for ( ; s < '0' || '9' < s; s = getchar () ); for ( ; '0' <= s && s <= '9'; s = getchar () ) x = x * 10 + ( s ^ '0' ); return x; } inline int max_ ( const int a, const int b ) { return a < b ? b : a; } struct PersistentSegmentTree { static const int MAXND = MAXN * 40; int cntnd, ch[MAXND + 5][2], sum[MAXND + 5], lmx[MAXND + 5], rmx[MAXND + 5]; inline void pushup ( const int rt ) { sum[rt] = sum[ch[rt][0]] + sum[ch[rt][1]]; lmx[rt] = max_ ( lmx[ch[rt][0]], sum[ch[rt][0]] + lmx[ch[rt][1]] ); rmx[rt] = max_ ( rmx[ch[rt][1]], sum[ch[rt][1]] + rmx[ch[rt][0]] ); } inline void build ( int& rt, const int l, const int r ) { rt = ++ cntnd; if ( l == r ) return sum[rt] = lmx[rt] = rmx[rt] = 1, void (); int mid = l + r >> 1; build ( ch[rt][0], l, mid ), build ( ch[rt][1], mid + 1, r ); pushup ( rt ); } inline void update ( int& rt, const int l, const int r, const int x ) { int old = rt; rt = ++ cntnd; ch[rt][0] = ch[old][0], ch[rt][1] = ch[old][1]; sum[rt] = sum[old], lmx[rt] = lmx[old], rmx[rt] = rmx[old]; if ( l == r ) return sum[rt] = -1, lmx[rt] = rmx[rt] = 0, void (); int mid = l + r >> 1; if ( x <= mid ) update ( ch[rt][0], l, mid, x ); else update ( ch[rt][1], mid + 1, r, x ); pushup ( rt ); } inline int qrySum ( const int rt, const int l, const int r, const int ql, const int qr ) { if ( ql <= l && r <= qr ) return sum[rt]; int mid = l + r >> 1, ret = 0; if ( ql <= mid ) ret += qrySum ( ch[rt][0], l, mid, ql, qr ); if ( mid < qr ) ret += qrySum ( ch[rt][1], mid + 1, r, ql, qr ); return ret; } inline pii qryLmx ( const int rt, const int l, const int r, const int ql, const int qr ) { if ( ql <= l && r <= qr ) return { lmx[rt], sum[rt] }; int mid = l + r >> 1, ret = 0, s = 0; pii tmp; if ( ql <= mid ) { tmp = qryLmx ( ch[rt][0], l, mid, ql, qr ); ret = tmp.first, s = tmp.second; } if ( mid < qr ) { tmp = qryLmx ( ch[rt][1], mid + 1, r, ql, qr ); ret = max_ ( ret, s + tmp.first ), s += tmp.second; } return { ret, s }; } inline pii qryRmx ( const int rt, const int l, const int r, const int ql, const int qr ) { if ( ql <= l && r <= qr ) return { rmx[rt], sum[rt] }; int mid = l + r >> 1, ret = 0, s = 0; pii tmp; if ( mid < qr ) { tmp = qryRmx ( ch[rt][1], mid + 1, r, ql, qr ); ret = tmp.first, s = tmp.second; } if ( ql <= mid ) { tmp = qryRmx ( ch[rt][0], l, mid, ql, qr ); ret = max_ ( ret, s + tmp.first ), s += tmp.second; } return { ret, s }; } } pst; int main () { n = rint (); for ( int i = 1; i <= n; ++ i ) a[i] = tval[i] = rint (); std::sort ( tval + 1, tval + n + 1 ); int lim = std::unique ( tval + 1, tval + n + 1 ) - tval - 1; for ( int i = 1; i <= n; ++ i ) { a[i] = std::lower_bound ( tval + 1, tval + lim + 1, a[i] ) - tval; apr[a[i]].push_back ( i ); } pst.build ( root[1], 1, n ); // 注意這裡n的意義成為了序列長度,不要與lim搞混。 for ( int i = 2; i <= lim; ++ i ) { root[i] = root[i - 1]; for ( int p: apr[i - 1] ) pst.update ( root[i], 1, n, p ); } for ( int q = rint (), ans = 0, tmp[4]; q --; ) { for ( int i = 0; i < 4; ++ i ) tmp[i] = ( rint () + ans ) % n + 1; std::sort ( tmp, tmp + 4 ); int l = 1, r = lim; while ( l <= r ) { int mid = l + r >> 1; int cnt = pst.qrySum ( root[mid], 1, n, tmp[1], tmp[2] ) + pst.qryLmx ( root[mid], 1, n, tmp[2] + 1, tmp[3] ).first + pst.qryRmx ( root[mid], 1, n, tmp[0], tmp[1] - 1 ).first; if ( cnt >= 0 ) l = ( ans = mid ) + 1; else r = mid - 1; } printf ( "%d\n", ans = tval[ans] ); } return 0; }