BZOJ 2653 middle
2016-06-17 11:52
183 查看
title: ‘BZOJ 2653 middle’
categories: BZOJ
date: 2016-1-6 19:30:00
tags: [主席树,二分]
给你一个长度为n的序列s。
回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数。
其中 a< b < c < d。
位置也从0开始标号。
我会使用一些方式强制你在线。
接下来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]。
输入保证满足条件。
5
170337785
271451044
22430280
969056313
206452321
3
3 1 0 2
2 3 1 4
3 1 4 0
output.txt
271451044
271451044
969056313
1,…,5:n<=2000
0,…,19:n<=20000,Q<=25000
我想了好久没想出来,然后又看题解看了好久,毕竟太菜。。。(orz神犇zyj随意秒了,无情d我)
要求中位数最大,而且区间端点要在两个给定的区间内,我们考虑二分这个中位数,然后转化成判定问题。
那么怎么判定某段区间内某个数x能不能作为中位数?
对于区间内的数,凡是>=x的数都标为1,否则标为-1,然后求出这个区间的最大子段和。若sum>=0,则该数肯定可以成为这个区间的中位数。求最大子段和,可以通过在线段树里维护一下lmax和rmax来求得,类似于里的操作。由于要求两端点分别在[a,b],[c,d]中,因此最大子段和就是T[a,b]rmax+T[b+1,c−1]sum+T[c,d]lmax。
线段树不可能建很多棵,主席树是很好的解决方案。我们发现大小相邻的两个数的线段树只相差一处地方,于是可以先对数字排序,然后按大小依次建树,初始所有的点都为1,然后依次修改为-1即可。
还有一个问题,要是我们二分出的中位数不在我们选中的区间内怎么办?
若二分出的数不在区间内且判定成功了,就说明区间内绝对有一个更大的符合要求的数,可以直接继续向上二分。
categories: BZOJ
date: 2016-1-6 19:30:00
tags: [主席树,二分]
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.txt5
170337785
271451044
22430280
969056313
206452321
3
3 1 0 2
2 3 1 4
3 1 4 0
output.txt
271451044
271451044
969056313
Hint
0:n,Q<=1001,…,5:n<=2000
0,…,19:n<=20000,Q<=25000
Solution
这道题真的挺难想的= =我想了好久没想出来,然后又看题解看了好久,毕竟太菜。。。(orz神犇zyj随意秒了,无情d我)
要求中位数最大,而且区间端点要在两个给定的区间内,我们考虑二分这个中位数,然后转化成判定问题。
那么怎么判定某段区间内某个数x能不能作为中位数?
对于区间内的数,凡是>=x的数都标为1,否则标为-1,然后求出这个区间的最大子段和。若sum>=0,则该数肯定可以成为这个区间的中位数。求最大子段和,可以通过在线段树里维护一下lmax和rmax来求得,类似于里的操作。由于要求两端点分别在[a,b],[c,d]中,因此最大子段和就是T[a,b]rmax+T[b+1,c−1]sum+T[c,d]lmax。
线段树不可能建很多棵,主席树是很好的解决方案。我们发现大小相邻的两个数的线段树只相差一处地方,于是可以先对数字排序,然后按大小依次建树,初始所有的点都为1,然后依次修改为-1即可。
还有一个问题,要是我们二分出的中位数不在我们选中的区间内怎么办?
若二分出的数不在区间内且判定成功了,就说明区间内绝对有一个更大的符合要求的数,可以直接继续向上二分。
Code
#include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #define maxn 20000+5 #define maxp 4000000+5 using namespace std; struct Ha_Tree{ int l,r; int lx,rx,sum; }tr[maxp]; int root[maxn],ori[maxn],pos[maxn]; int n,m,ind; bool cmp(int s,int b){ return ori[s]<ori[b]; } void Update(int k){ int l=tr[k].l,r=tr[k].r; tr[k].sum=tr[l].sum+tr[r].sum; tr[k].lx=max(tr[l].lx,tr[l].sum+tr[r].lx); tr[k].rx=max(tr[r].rx,tr[r].sum+tr[l].rx); } void Modify(int l,int r,int last,int &k,int pos,int val){ k=++ind; tr[k]=tr[last]; if(l==r){ tr[k].sum=tr[k].lx=tr[k].rx=val; return; } int mid=(l+r)>>1; if(pos<=mid) Modify(l,mid,tr[last].l,tr[k].l,pos,val); else Modify(mid+1,r,tr[last].r,tr[k].r,pos,val); Update(k); } int Query_all(int k,int l,int r,int pl,int pr){ if(l==pl && r==pr) return tr[k].sum; int mid=(l+r)>>1; if(pr<=mid) return Query_all(tr[k].l,l,mid,pl,pr); else if(pl>mid) return Query_all(tr[k].r,mid+1,r,pl,pr); else return Query_all(tr[k].l,l,mid,pl,mid)+Query_all(tr[k].r,mid+1,r,mid+1,pr); } int Query_l(int k,int l,int r,int pl,int pr){ if(l==pl && r==pr) return tr[k].lx; int mid=(l+r)>>1; if(pr<=mid) return Query_l(tr[k].l,l,mid,pl,pr); else if(pl>mid) return Query_l(tr[k].r,mid+1,r,pl,pr); else return max(Query_l(tr[k].l,l,mid,pl,mid),Query_all(tr[k].l,l,mid,pl,mid)+Query_l(tr[k].r,mid+1,r,mid+1,pr)); } int Query_r(int k,int l,int r,int pl,int pr){ if(l==pl && r==pr) return tr[k].rx; int mid=(l+r)>>1; if(pr<=mid) return Query_r(tr[k].l,l,mid,pl,pr); else if(pl>mid) return Query_r(tr[k].r,mid+1,r,pl,pr); else return max(Query_r(tr[k].r,mid+1,r,mid+1,pr),Query_all(tr[k].r,mid+1,r,mid+1,pr)+Query_r(tr[k].l,l,mid,pl,mid)); } bool check(int k,int a,int b,int c,int d){ int res=0; if(b+1!=c) res+=Query_all(root[k],1,n,b+1,c-1); res+=Query_r(root[k],1,n,a,b); res+=Query_l(root[k],1,n,c,d); if(res>=0) return true; return false; } int find(int a,int b,int c,int d){ int l=1,r=n; while(l<r){ int mid=(l+r+1)>>1; if(check(mid,a,b,c,d)) l=mid; else r=mid-1; } return ori[pos[l]]; } void Build(int l,int r,int &k){ k=++ind; if(l==r){ tr[k].lx=tr[k].rx=tr[k].sum=1; return; } int mid=(l+r)>>1; Build(l,mid,tr[k].l); Build(mid+1,r,tr[k].r); Update(k); } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&ori[i]),pos[i]=i; sort(pos+1,pos+1+n,cmp); Build(1,n,root[1]); for(int i=2;i<=n;i++) Modify(1,n,root[i-1],root[i],pos[i-1],-1); scanf("%d",&m); int x=0; for(int i=1;i<=m;i++){ int q[4]; scanf("%d%d%d%d",&q[0],&q[1],&q[2],&q[3]); for(int i=0;i<4;i++) q[i]=(q[i]+x)%n+1; sort(q,q+4); printf("%d\n",x=find(q[0],q[1],q[2],q[3])); } return 0; }
相关文章推荐
- 快速排序里的学问:从猜数字开始
- HDU 4898 The Revenge of the Princess’ Knight ( 2014 Multi-University Training Contest 4 )
- Search Insert Position,Search for a Range,Pow(x, n),Sqrt(x)
- Find Minimum in Rotated Sorted Array II
- [LeetCode] Sqrt(x)
- [LeetCode] Pow(x, n)
- [LeetCode] Search Insert Position
- [LeetCode] Search for a Range
- [LeetCode] Search in Rotated Sorted Array
- PAT 1057 Stack (30)
- int sqrt(int x)
- Pow(x, n)
- Find Minimum in Rotated Sorted Array
- Divide Two Integers
- 信息竞赛学习笔记:POJ3579中位数(二分)
- acm解题报告 HDU 2141 Can you find it?
- acm解题报告 HDU 2199 Can you solve this equation?
- acm解题报告 HDU 2899 Strange fuction
- acm解题报告 HDU 1969 Pie
- acm解题报告 HDU 1061 Rightmost Digit