【主席树】2012集训队互测 Middle
来源:互联网 发布:数据分析师主要做什么 编辑:程序博客网 时间:2024/05/22 15:03
JZOJ 2902 集训队互测2012 middle(陈立杰)
【题目大意】给定一个序列,每次询问左端点在[a,b]中,右端点在[c,d]中的子序列中,最大的中位数。强制在线。
【题解】
这题虽然不是这几天做的,但是最近在搞数据结构,再总结一下还是会有收获的。(复习时应该看看)
设一个序列S从大到小排序后为S[1..k],则中位数为M=S[k/2向上取整]。
那就意味着,在序列S中,大于等于M的数大于等于k/2。如果把序列S转化为T,T[i]=1(S[i]>=M),-1(S[i]<M),则ΣT[i]一定非负。
这样我们很容易想到一个算法:二分答案+主席树
1.先按位置建一棵叶子节点权值为1的原始线段树(第一个版本的线段树)。要维护区间和sum,区间左侧最大前缀和lsum,和右侧最大前缀和rsum。
2.然后按权值从小到大的顺序依次在相应的位置修改,每次新建一个版本的线段树(可持久化)
假设当前到第i个版本的线段树,则要把第i-1大的数的位置权值修改为-1。这样在第i个版本的线段树上,比第i大的数还大(或等于)的数的位置上值为1,否则为-1。
3.二分答案。
二分到k时,就在第k个版本的线段树上查询。(程序实现时root[0]为第一个版本线段树的根)。
若Query(root[k-1],1,N,ask[1],ask[2]).rsum + Query(root[k-1],1,N,ask[2]+1,ask[3]-1).sum + Query(root[k-1],1,N,ask[3],ask[4]).lsum >= 0,就说明k可以为中位数,继续二分。
#include<cstdio>#include<cstring>#include<algorithm>#include<cstdlib>#include<cmath>#define fo(i,a,b) for (int i = a;i <= b;i ++)#define fd(i,a,b) for (int i = a;i >= b;i --)using namespace std;const int maxn = 2000000;struct node{int lsum,rsum,sum,l,r;node(){}node(int a,int b,int c) {lsum = a,rsum = b,sum = c,l = r = 0;}};int root[maxn];node tree[maxn];int a[maxn],b[maxn],pos[maxn];int N,Q,TOT,n,Last;int ask[10];bool cmp(int x,int y) { return a[x] < a[y]; }void Readin(){scanf("%d",&N);fo(i,1,N) scanf("%d",&a[i]),pos[i] = i;}void Discretize(){ sort(pos+1,pos+N+1,cmp); }void Update(int z){int lson = tree[z].l;int rson = tree[z].r;tree[z].lsum = max(tree[lson].sum + tree[rson].lsum,tree[lson].lsum);tree[z].rsum = max(tree[rson].sum + tree[lson].rsum,tree[rson].rsum);tree[z].sum = tree[lson].sum + tree[rson].sum;}void AddPoint(int &z,int x,int l,int r,int p){z = ++ TOT;if (l == r) {tree[z].l = tree[z].r = 0;tree[z].lsum = tree[z].rsum = -1;tree[z].sum = -1;} else {int Mid = (l + r) >> 1;if (p <= Mid) {AddPoint(tree[z].l,tree[x].l,l,Mid,p);tree[z].r = tree[x].r;} else {AddPoint(tree[z].r,tree[x].r,Mid+1,r,p);tree[z].l = tree[x].l;}Update(z);}}void Filltree(int &z,int l,int r){z = ++ TOT;if (l == r){tree[z].l = tree[z].r = 0;tree[z].lsum = tree[z].rsum = 1;tree[z].sum = 1;} else {int Mid = (l + r) >> 1;Filltree(tree[z].l,l,Mid);Filltree(tree[z].r,Mid+1,r);Update(z);}}void Maketree(){TOT = 0;Filltree(root[0],1,N);fo(i,1,N-1) AddPoint(root[i],root[i-1],1,N,pos[i]);}void Initialize(){Readin();Discretize();Maketree();}node Query(int z,int l,int r,int x,int y){if (y < x) return node(0,0,0);if (l == x && r == y) return tree[z];int Mid = (l + r) >> 1;if (y <= Mid) return Query(tree[z].l,l,Mid,x,y);else if (x > Mid) return Query(tree[z].r,Mid+1,r,x,y);else {node ret,retl,retr;retl = Query(tree[z].l,l,Mid,x,Mid);retr = Query(tree[z].r,Mid+1,r,Mid+1,y);ret.lsum = max(retl.sum + retr.lsum,retl.lsum);ret.rsum = max(retr.sum + retl.rsum,retr.rsum);ret.sum = retl.sum + retr.sum;return ret;}}bool Check(int k){return Query(root[k-1],1,N,ask[1],ask[2]).rsum + Query(root[k-1],1,N,ask[2]+1,ask[3]-1).sum + Query(root[k-1],1,N,ask[3],ask[4]).lsum >= 0;}void Work(){int L,R,Mid;L = 1;R = N;while (L < R){Mid = (L + R + 1) >> 1;if (Check(Mid)) L = Mid;else R = Mid - 1;}Last = a[pos[L]];printf("%d\n",a[pos[L]]);}int main(void){Initialize();scanf("%d",&Q);Last = 0;while (Q){fo(i,1,4) scanf("%d",&ask[i]);fo(i,1,4) ask[i] = (ask[i] + Last) % N + 1;sort(ask+1,ask+5);Work();Q --;}return 0;}
0 0
- 【主席树】2012集训队互测 Middle
- 【集训队互测 2012】Middle
- 【集训队互测 2012】Middle
- 【jzoj2902】【集训队互测 2012】【Middle】【可持久化线段树】
- [BZOJ2653]middle-主席树
- 【BZOJ2653】【二分法】【主席树】middle
- 【主席树】 BZOJ 2653 middle
- 【主席树】BZOJ 2653 middle
- bzoj2653 Middle 二分&主席树
- BZOJ 2653: middle|主席树
- [主席树+二分] BZOJ2653: middle
- bzoj2653: middle 主席树+线段树
- Bzoj 2653 middle(二分+主席树)
- BZOJ 2653 middle 二分+主席树
- 【bzoj2653】【middle】【主席树+二分答案】
- HYSBZ 2653middle(二分+主席树)
- [主席树 二分答案] BZOJ 2653 middle
- [BZOJ2653]middle(二分+主席树)
- sort命令
- Linux重定向介绍
- stringstream用法
- 连网
- AOJ-AHU-OJ-453 棋盘问题(位压缩)
- 【主席树】2012集训队互测 Middle
- Android - 使用资源(resource)
- JavaSE 反射Reflection初学
- 颜色渐变的弧度条
- 易飞小数点后位数
- 源代码Bwriter.java,从键盘输入一系列字符串,写入到磁盘(修改版)
- Algorithm学习笔记 --- 最大乘积简单枚举
- GLSL varying variable
- 基于行为树与状态机的游戏人工智能