二分树状数组-洛谷P1168 中位数
2017-02-23 18:43
274 查看
https://www.luogu.org/problem/show?pid=1168
本来和fopzz想练练树状数组,就搜索到了这道题;
TM二分的条件搞了一个下午;
基本思想就是二分寻找答案,然后用树状数组去维护有几个比这个二分出来的值大,然后就没有了;
数据要离散,这个好像用map也可以,但是不推荐;
我一开始TLE然后就以为是map慢,结果改好后是降了600ms;
那怎么离散呢?
我们先把a数组读入并复制给b数组;
然后排序a;
这个时候a数组就有序了,我们就可以把b数组里的值通过二分找到其在a数组里的下标,这样就把1~1e9的数据压缩到1e5了;
这样的离散支持去重,支持不去重;
离散好了我们咋么搞呢?
树状数组怎么来维护比x小的数的个数呢?;
我们能用树状数组来维护前=前缀和;
那我们每增加一个数,我们就把他当作下标,在上面+1;
然后我统计小于等于x的个数时直接取x的前缀和好了;
对于二分求答案,从1~n里二分;
因为离散后数据一定再1~n里面;
对于一个数吧,有三种情况
没出现过这个数
这个数恰好有一个
这个数有很多
因为个数是奇数,所以我们枚举到一个空数时,比他小的个数必然不等于比他大的,可以继续二分;
然后恰好有一个,我们直接找到有几个比他小,如果是总数div2就是答案了;
如果有多个,设数是x,那显然 <=x的 减 小于x的 大于1;
这时如果<=x大于总数div2,小于x的小于总数div2,答案就是x;
本来和fopzz想练练树状数组,就搜索到了这道题;
TM二分的条件搞了一个下午;
基本思想就是二分寻找答案,然后用树状数组去维护有几个比这个二分出来的值大,然后就没有了;
数据要离散,这个好像用map也可以,但是不推荐;
我一开始TLE然后就以为是map慢,结果改好后是降了600ms;
那怎么离散呢?
我们先把a数组读入并复制给b数组;
然后排序a;
这个时候a数组就有序了,我们就可以把b数组里的值通过二分找到其在a数组里的下标,这样就把1~1e9的数据压缩到1e5了;
这样的离散支持去重,支持不去重;
离散好了我们咋么搞呢?
树状数组怎么来维护比x小的数的个数呢?;
我们能用树状数组来维护前=前缀和;
那我们每增加一个数,我们就把他当作下标,在上面+1;
然后我统计小于等于x的个数时直接取x的前缀和好了;
对于二分求答案,从1~n里二分;
因为离散后数据一定再1~n里面;
对于一个数吧,有三种情况
没出现过这个数
这个数恰好有一个
这个数有很多
因为个数是奇数,所以我们枚举到一个空数时,比他小的个数必然不等于比他大的,可以继续二分;
然后恰好有一个,我们直接找到有几个比他小,如果是总数div2就是答案了;
如果有多个,设数是x,那显然 <=x的 减 小于x的 大于1;
这时如果<=x大于总数div2,小于x的小于总数div2,答案就是x;
#include<cstdio>//cfb #include<cstdlib> #include<iostream> #include<algorithm> #include<cmath> #include<map> using namespace std; int a[100001],b[100001],c[100001]; bool vi[100001]; int n,ans,nn,x; void add(int x,int y){ for(int i=x;i<=n;i+=i&-i)c[i]+=y; } int out(int x){ int ans=0;for(int i=x;i;i-=i&-i)ans+=c[i];return ans; } void outit(int num){ int l=1,r=n,ans; while(r>=l){ int mid=l+r>>1; int sum=out(mid-1); int s=out(mid);//为什么这里是>而不是>=,因为s包含答案自己,要-1 if(vi[mid]&&((sum<num&&s>num)||sum==num)){ans=mid;break;} if(sum>num)r=mid-1;else l=mid+1; } printf("\n%d",a[ans]); } int er(int x){ int l=1,r=n,ans; while(1){ int mid=l+r>>1; if(a[mid]==x)return mid; if(a[mid]>x)r=mid-1;else l=mid+1; } } int main(){ scanf("%d",&n); if(!(n&1))n--; for(int i=1;i<=n;i++)scanf("%d",&a[i]),b[i]=a[i];x=a[1]; sort(a+1,a+n+1); for(int i=1;i<=n;i++)b[i]=er(b[i]); printf("%d",x); add(b[1],1);vi[b[1]]=1; for(int i=3;i<=n;i+=2){ add(b[i-1],1); add(b[i],1); vi[b[i-1]]=vi[b[i]]=1; outit(i>>1); } }
相关文章推荐
- ACM学习历程—51NOD 1685 第K大区间2(二分 && 树状数组 && 中位数)
- 【leetcode】寻找两个已排序数组的中位数(类似二分)
- Hdu 5493 Queue【伸展树/二分+树状数组】
- BZOJ2738【整体二分】【树状数组】
- Luogu 2680 NOIP 2015 运输计划(树链剖分,LCA,树状数组,树的重心,二分,差分)
- BZOJ 2738: 矩阵乘法 整体二分 二维树状数组
- 多数组中位数,k大数 -- 二分思路
- PAT (Advanced Level) 1029. Median (25) 求两个有序数组的中位数,二分
- ZOJ 3635 Cinema in Akiba(树状数组 + 二分)
- [二分+树状数组]51 Nod 1685——第K大区间2
- hdu 2852(树状数组+二分)
- 【CF】C. Glass Carving(二分 + 树状数组 + 优先队列 + 数组计数)
- 【poj 2104】K-th Number【整体二分+树状数组】
- 【BZOJ】2527 [Poi2011]Meteors 整体二分+树状数组
- 区间交(树状数组+二分)hdu5700
- POJ 2104 K-th Number【整体二分 + 树状数组】
- HDU 5592 ZYB's Premutation(树状数组+二分)
- hdu5338 贪心+线段树+二分+树状数组
- [LeetCode]Median of Two Sorted Arrays 二分查找两个有序数组的第k数(中位数)
- POJ - 2886 Who Gets the Most Candies? 树状数组 + 二分 + 反素数