[國家集訓隊]middle(二分+主席樹[中位數思維題])
阿新 • • 發佈:2021-01-06
技術標籤:# 線段樹及可持久化二分/三分主席樹可持久化線段樹二分法中位數思維
文章目錄
點選檢視
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,c−1]
即,只需要最大化
[
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;
}