bzoj2653 middle
2653: middle
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 2091 Solved: 1152
[Submit][Status][Discuss]
Description
Input
Output
Q行依次给出询问的答案。
Sample
170337785
271451044
22430280
969056313
206452321
3
3 1 0 2
2 3 1 4
3 1 4 0
271451044
271451044
969056313
0:n,Q<=100
1,...,5:n<=2000
0,...,19:n<=20000,Q<=25000
分析:挺难有思路的一道题.
要求最大的中位数,我的第一想法是枚举找出所有可能的中位数,但这明显不行嘛.
既然不能直接找,那能不能假设我找到了这个最大的中位数,然后判断它是否可行呢?
emm,似乎可以,但是要怎么判断啊?利用中位数的定义,设目前枚举的中位数为x,≥x的数标记为1,<x的数标记为-1,如果一段区间里的数的和加起来≥0,则证明x可以是中位数.
why?其实很容易想到如果和>=0,那么x肯定不会小于中位数,但有可能大于中位数,如果大于中位数,接着枚举x+1就可以了.枚举到的最大的满足条件的x就是最大的中位数.显然这个枚举可以替换成二分查找.
现在已经知道了如何求一段固定的区间的最大的中位数,但是题目中的区间并不是固定的,难道我们要去枚举区间吗?
一个很简单的转化:最大连续子段和:[b,c]的和+[a,b - 1]的最大右子段和+[c + 1,d]的最大左子段和就是要求的.而求最大子段和可以利用线段树来维护.
还有一个问题就是:我二分的x是会变的,每次都要重新标记一遍吗?
别忘了,还有一个利器:主席树.只不过这个主席树和我平时写的主席树都不太一样.这是个利用权值大小来插入,维护“下标”线段树的权值线段树.因为最后的查询给定的是下标.如果用权值线段树,点的分布都不是连续的.非常巧妙,和我以前做过的一道splay题的思想差不多:传送门.
最后是一些坑点:下标从0开始,不需要去重,左右最大子段和的端点b,c要变成b-1,c+1来查询,查询操作的写法要注意.
总之,我认为这是一道比较神的题,颠覆了我对主席树的认识与理解.学到了中位数的判定方法和单调性,还学了一种奇怪的主席树的写法.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 20010; int n,a[maxn],tot,root[maxn],b[maxn],id[maxn]; int sum[maxn * 20],lsum[maxn * 20],rsum[maxn * 20]; int lson[maxn * 20],rson[maxn * 20],q,lastans,qq[5]; int aa,bb,cc,dd; void pushup(int o) { int ls = lson[o],rs = rson[o]; sum[o] = sum[ls] + sum[rs]; rsum[o] = max(rsum[rs],sum[rs] + rsum[ls]); lsum[o] = max(lsum[ls],sum[ls] + lsum[rs]); } void build(int &o,int l,int r) { o = ++tot; if (l == r) { sum[o] = lsum[o] = rsum[o] = 1; return; } int mid = (l + r) >> 1; build(lson[o],l,mid); build(rson[o],mid + 1,r); pushup(o); } void insert(int l,int r,int x,int &y,int pos,int v) { y = ++tot; if (l == r) { sum[y] = lsum[y] = rsum[y] = v; return; } lson[y] = lson[x]; rson[y] = rson[x]; int mid = (l + r) >> 1; if (pos <= mid) insert(l,mid,lson[x],lson[y],pos,v); else insert(mid + 1,r,rson[x],rson[y],pos,v); pushup(y); } int query2(int l,int r,int x,int y,int o) { if (x > y) return 0; if (x <= l && r <= y) return sum[o]; int mid = (l + r) >> 1,res = 0; if (x <= mid) res += query2(l,mid,x,y,lson[o]); if (y > mid) res += query2(mid + 1,r,x,y,rson[o]); return res; } int query1(int l,int r,int x,int y,int o) { if (x > y) return 0; if (l == x && y == r) return rsum[o]; int mid = (l + r) >> 1; if (y <= mid) return query1(l,mid,x,y,lson[o]); else if (x > mid) return query1(mid + 1,r,x,y,rson[o]); else return max(query1(mid + 1,r,mid + 1,y,rson[o]),query2(mid + 1,r,mid + 1,y,rson[o]) + query1(l,mid,x,mid,lson[o])); } int query3(int l,int r,int x,int y,int o) { if (x > y) return 0; if (l == x && y == r) return lsum[o]; int mid = (l + r) >> 1; if (y <= mid) return query3(l,mid,x,y,lson[o]); else if (x > mid) return query3(mid + 1,r,x,y,rson[o]); else return max(query3(l,mid,x,mid,lson[o]),query2(l,mid,x,mid,lson[o]) + query3(mid + 1,r,mid + 1,y,rson[o])); } bool check(int x) { return (max(query1(1,n,aa,bb - 1,root[x]),0) + query2(1,n,bb,cc,root[x]) + max(query3(1,n,cc + 1,dd,root[x]),0)) >= 0; } bool cmp(int x,int y) { return a[x] < a[y]; } int main() { scanf("%d",&n); for (int i = 1; i <= n; i++) { scanf("%d",&a[i]); id[i] = i; } memcpy(b,a,sizeof(a)); stable_sort(id + 1,id + n + 1,cmp); sort(b + 1,b + 1 + n); build(root[1],1,n); for (int i = 2; i <= n; i++) insert(1,n,root[i - 1],root[i],id[i - 1],-1); scanf("%d",&q); while (q--) { scanf("%d%d%d%d",&aa,&bb,&cc,&dd); qq[1] = (aa + lastans) % n + 1; qq[2] = (bb + lastans) % n + 1; qq[3] = (cc + lastans) % n + 1; qq[4] = (dd + lastans) % n + 1; sort(qq + 1,qq + 5); aa = qq[1],bb = qq[2],cc = qq[3],dd = qq[4]; int ans = 0,l = 1,r = n; while (l <= r) { int mid = (l + r) >> 1; if (check(mid)) { ans = mid; l = mid + 1; } else r = mid - 1; } lastans = b[ans]; printf("%d\n",lastans); } return 0; }