2653: middle
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 1855 Solved: 1031
[ Submit][ Status][ Discuss]
Description
一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整。给你一个
长度为n的序列s。回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数。
其中a<b<c<d。位置也从0开始标号。我会使用一些方式强制你在线。
Input
第一行序列长度n。接下来n行按顺序给出a中的数。
接下来一行Q。然后Q行每行a,b,c,d,我们令上个询问的答案是
x(如果这是第一个询问则x=0)。
令数组q={(a+x)%n,(b+x)%n,(c+x)%n,(d+x)%n}。
将q从小到大排序之后,令真正的
要询问的a=q[0],b=q[1],c=q[2],d=q[3]。
输入保证满足条件。
第一行所谓“排过序”指的是从大到小排序!
Output
Q行依次给出询问的答案。
Sample Input
5
170337785
271451044
22430280
969056313
206452321
3
3 1 0 2
2 3 1 4
3 1 4 0
271451044
271451044
969056313
170337785
271451044
22430280
969056313
206452321
3
3 1 0 2
2 3 1 4
3 1 4 0
271451044
271451044
969056313
Sample Output
HINT
0:n,Q<=100
1,...,5:n<=2000
0,...,19:n<=20000,Q<=25000
Source
clj的题目果然难啊。。。膜拜各种题解之后写出来的
如果只是求某区间中位数的话考虑可以使用主席树维护权值然后bst一下
然而多了个最大,所以就要二分答案(sb没有想到,基础不够扎实)
二分答案x后如果是定区间只需要验证方案的合法,而动区间则需要构造函数来判断这个值是否可以增大。
显然中位数是相对大小的产物,因此用1代表大于等于x,-1代表小于x,那么对与某个区间的一段转换为1和-1的子区间,如果它的和大于0,显然中位数要更大。很经典的转化方法。
问题到目前为止转化为:给定一个序列,序列中的数均是1或-1,求起始下标为[a,b],终止下标为[c,d]所有子序列中最大的子序列和是否大于0。这显然是一个最大字段和问题。
发现(b,c)是必取的,而最大化的目标可以转化为已b为终止下标在区间[a,b]上的最大字段和和以c为起始下标在区间[c,d]上的最大子段和。这显然对应的是线段树中维护左子区间最大和右子区间最大的做法。
最后一个问题就是,对于不同的x如何维护它对应的线段树。我们发现,如果x转移到x的后继,那么只有一个值x会发生变化。这样就可以直接套用主席树模板。具体来说:先把初始每个数设置为1,然后从小到大吧每个数插入一颗以位置主席树中并将其改成-1
整理一下做法和思路:对于最大的中位数,先构造根据每个数在原区间上的相对大小的序列,及大于等于这个数就为1,否则为-1,这个用主席树维护,然后二分答案x,判断以x为中位数可大还是可小,在主席树上找(b,c)和rm[a,b]和lm[c,d]相加,若大于零则x可能增大,小于零则x必须减小。
一道高级数据结构里蕴含了动态规划与构造性的思想,实在是一道难得的好题。
/**************************************************************
Problem: 2653
User: 2014lvzelong
Language: C++
Result: Accepted
Time:2264 ms
Memory:14168 kb
****************************************************************/
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<cmath>
#define maxn 32000
#define inf 0x3f3f3f3f
using namespace std;
int read()
{
char ch = getchar(); int x = 0, f = 1;
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return x * f;
}
struct data {
int val, id;
bool operator < (const data &a) const {
return val == a.val ? id < a.id : val < a.val;
}
}a[maxn];
int n, m, sz, root[maxn];
int sum[maxn * 20], rs[maxn * 20], ls[maxn * 20], lm[maxn * 20], rm[maxn * 20];
void update(int p) {
sum[p] = sum[ls[p]] + sum[rs[p]];
lm[p] = max(lm[ls[p]], sum[ls[p]] + lm[rs[p]]);
rm[p] = max(rm[rs[p]], sum[rs[p]] + rm[ls[p]]);
}
void build(int &p, int L, int R) {
p = ++sz;
if(L == R) {
sum[p] = lm[p] = rm[p] = 1;
return;
}
int mid = L + R >> 1;
build(ls[p], L, mid);
build(rs[p], mid + 1, R);
update(p);
}
void add_tree(int &cur_p, int pre_p, int pos, int L, int R, int val) {
cur_p = ++sz;
if(L == R) {
sum[cur_p] = lm[cur_p] = rm[cur_p] = val;
return;
}
int mid = L + R >> 1;
if(pos <= mid) {
rs[cur_p] = rs[pre_p];
add_tree(ls[cur_p], ls[pre_p], pos, L, mid, val);
}
else {
ls[cur_p] = ls[pre_p];
add_tree(rs[cur_p], rs[pre_p], pos, mid + 1, R, val);
}
update(cur_p);
}
void init() {
n = read();
for(int i = 0;i < n; ++i) {
scanf("%d", &a[i].val);
a[i].id = i;
}
sort(a, a + n); lm[0] = rm[0] = -inf;
build(root[0], 0, n - 1);
for(int i = 1;i < n; ++i) add_tree(root[i], root[i - 1], a[i - 1].id, 0, n - 1, -1);
}
int query_sum(int p, int st, int ed, int L, int R) {
if(st > ed) return 0;
if(st == L && ed == R) return sum[p];
int ans = 0, mid = L + R >> 1;
if(st <= mid) ans += query_sum(ls[p], st, min(mid, ed), L, mid);
if(ed > mid) ans += query_sum(rs[p], max(mid + 1, st), ed, mid + 1, R);
return ans;
}
int query_lm(int p, int st, int ed, int L, int R) {
if(st == L && ed == R) return lm[p];
int ans = -inf, mid = L + R >> 1;
if(st <= mid) ans = max(ans, query_lm(ls[p], st, min(ed, mid), L, mid));
if(ed > mid) ans = max(ans, query_sum(ls[p], st, mid, L, mid) + query_lm(rs[p], max(mid + 1, st), ed, mid + 1, R));
return ans;
}
int query_rm(int p, int st, int ed, int L, int R) {
if(st == L && ed == R) return rm[p];
int ans = -inf, mid = L + R >> 1;
if(ed > mid) ans = max(ans, query_rm(rs[p], max(mid + 1, st), ed, mid + 1, R));
if(st <= mid) ans = max(ans, query_sum(rs[p], mid + 1, ed, mid + 1, R) + query_rm(ls[p], st, min(ed, mid), L, mid));
return ans;
}
bool judge(int cur, int a, int b, int c, int d) {
int ret = query_sum(root[cur], b + 1, c - 1, 0, n - 1);
ret += query_rm(root[cur], a, b, 0, n - 1);
ret += query_lm(root[cur], c, d, 0, n - 1);
return ret >= 0;
}
void solve() {
m = read(); int ans = 0, q[4];
while(m--) {
for(int i = 0;i < 4; ++i) q[i] = (read() + ans) % n;
sort(q, q + 4);
int L = 0, R = n - 1;
while(L < R) {
int mid = L + R + 1 >> 1;
if(judge(mid, q[0], q[1], q[2], q[3])) L = mid;
else R = mid - 1;
}
printf("%d\n", ans = a[L].val);
}
}
int main()
{
init();
solve();
return 0;
}