1. 程式人生 > 其它 >[國家集訓隊]middle(二分+主席樹[中位數思維題])

[國家集訓隊]middle(二分+主席樹[中位數思維題])

技術標籤:# 線段樹及可持久化二分/三分主席樹可持久化線段樹二分法中位數思維

文章目錄

點選檢視

solution

簡單口胡一下就跑
在這裡插入圖片描述

考慮二分答案 a n s ans ans
區間 [ x 1 , x 2 ] , x 1 ∈ [ a , b ] , x 2 ∈ [ c , d ] [x1,x2],x1∈[a,b],x2∈[c,d] [x1,x2],x1[a,b]x2[c,d]
大於等於 a n s ans ans的設為 1 1 1,小於 a n s ans ans的設為 0 0 0
如果和 ≥ 0 \ge 0 0表示 a n s ans ans還可以更大,否則應該變小

發現無論 x 1 , x 2 x1,x2 x1,x2怎麼取,有一段是肯定包含的 → [ b + 1 , c − 1 ] \rightarrow[b+1,c-1] [b+1,c1]
即,只需要最大化 [ x 1 , b ] , [ c , x 2 ] [x1,b],[c,x2] [x1,b],[c,x2]之間的和,就是一個最大字尾/最大字首問題

可以利用線段樹求解
對於每一箇中位數建立一棵線段樹
發現空間飛了
主席樹硬剛
在這裡插入圖片描述

值域遠大於 “ “ 定義域 ” ”
離散搞 t a ta ta
在這裡插入圖片描述

詳情請見下方程式碼
⇓ \Downarrow

code

#include <cstdio>
#include <iostream> #include <algorithm> using namespace std; #define maxn 30000 #define int long long struct node { int l, r, sum, lsum, rsum; }t[maxn * 25]; int n, Q, cnt, last; int a[maxn], id[maxn], tmp[maxn], root[maxn], opt[5]; void push_up( int num ) { int l = t[num].l, r = t[num]
.r; t[num].sum = t[l].sum + t[r].sum; t[num].lsum = max( t[l].lsum, t[l].sum + t[r].lsum ); t[num].rsum = max( t[r].rsum, t[r].sum + t[l].rsum ); } void build( int &num, int l, int r ) { if( ! num ) num = ++ cnt; if( l == r ) { t[num].sum = t[num].lsum = t[num].rsum = r - l + 1; return; } int mid = ( l + r ) >> 1; build( t[num].l, l, mid ), build( t[num].r, mid + 1, r ); push_up( num ); } void modify( int pre, int &now, int l, int r, int pos ) { if( ! now ) now = ++ cnt; if( l == r ) { t[now].sum = t[now].lsum = t[now].rsum = -1; return; } int mid = ( l + r ) >> 1; if( pos <= mid ) { if( ! t[now].r ) t[now].r = t[pre].r; modify( t[pre].l, t[now].l, l, mid, pos ); } else { if( ! t[now].l ) t[now].l = t[pre].l; modify( t[pre].r, t[now].r, mid + 1, r, pos ); } push_up( now ); } node query( int num, int l, int r, int L, int R ) { if( L <= l && r <= R ) return t[num]; int mid = ( l + r ) >> 1; if( R <= mid ) return query( t[num].l, l, mid, L, R ); else if( mid < L ) return query( t[num].r, mid + 1, r, L, R ); else { node ans1 = query( t[num].l, l, mid, L, R ); node ans2 = query( t[num].r, mid + 1, r, L, R ); node ans; ans.sum = ans1.sum + ans2.sum; ans.lsum = max( ans1.lsum, ans1.sum + ans2.lsum ); ans.rsum = max( ans2.rsum, ans2.sum + ans1.rsum ); return ans; } } bool check( int x ) { int ans = 0; if( opt[2] + 1 <= opt[3] - 1 ) ans += query( root[x], 1, n, opt[2] + 1, opt[3] - 1 ).sum; ans += query( root[x], 1, n, opt[1], opt[2] ).rsum; ans += query( root[x], 1, n, opt[3], opt[4] ).lsum; return ans >= 0; } bool cmp( int x, int y ) { return a[x] < a[y]; } signed main() { scanf( "%lld", &n ); build( root[1], 1, n ); for( int i = 1;i <= n;i ++ ) scanf( "%lld", &a[i] ), id[i] = i; sort( id + 1, id + n + 1, cmp ); for( int i = 2;i <= n;i ++ ) modify( root[i - 1], root[i], 1, n, id[i - 1] ); scanf( "%lld", &Q ); while( Q -- ) { for( int i = 1;i <= 4;i ++ ) { scanf( "%lld", &opt[i] ); opt[i] = ( opt[i] + last ) % n + 1; } sort( opt + 1, opt + 5 ); int l = 1, r = n, ans; while( l <= r ) { int mid = ( l + r ) >> 1; if( check( mid ) ) ans = mid, l = mid + 1; else r = mid - 1; } last = a[id[ans]]; printf( "%lld\n", last ); } return 0; }