[CQOI]中位数 && 中位数(qbxt) && 众数(qbxt)
给出1~n的一个排列,统计该排列有多少个长度为奇数的连续子序列的中位数是b。中位数是指把所有元素从小到大排列后,位于中间的数。
输入输出格式
输入格式:
第一行为两个正整数n和b,第二行为1~n的排列。
【数据规模】
对于30%的数据中,满足n≤100;
对于60%的数据中,满足n≤1000;
对于100%的数据中,满足n≤100000,1≤b≤n。
输出格式:
输出一个整数,即中位数为b的连续子序列个数。
from 黄学长
找到b在数列中的位置设为point,比b大的赋值为-1,比b小的赋值为1;
然后求出sum[i,point]的值出现了几次记为lfre[sum[i,point]]++; ans += lfre[sum[i,point]]*rfre[-sum[i,point]];
由于c++数组不能是负数,所以稍微处理一下
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 const int maxn=1e6+7; 5 int n,b,tmp,ans; 6 int a[maxn],sum[maxn],lft[maxn],rgt[maxn],t[maxn]; 7 int main(){ 8 cin>>n>>b; 9 for(int i=1;i<=n;i++) cin>>a[i]; 10 for(int i=1;i<=n;i++){ 11 if(a[i]>b) t[i]=1; 12 if(a[i]==b){t[i]=0;tmp=i;} 13 if(a[i]<b) t[i]=-1; 14 } 15 lft[n]=1;rgt[n]=1; 16 for(int i=tmp-1;i>=1;i--){ 17 sum[i]=sum[i+1]+t[i];lft[sum[i]+n]++; 18 } 19 for(int i=tmp+1;i<=n;i++){ 20 sum[i]=sum[i-1]+t[i];rgt[sum[i]+n]++; 21 } 22 for(int i=0;i<2*n;i++){ 23 ans+=lft[i]*rgt[2*n-i]; 24 } 25 cout<<ans<<endl; 26 return 0; 27 }
第k大一般用二分
二分一个答案,将序列中大于它的数附为1,小于它的赋为-1,利用树状数组求顺序对,即中位数大于当前答案的区间的个数
如果这个值大了,说明当前二分的答案小了,就要r=mid+1当然这里指的是在数列中的下标
1 #include <cstdio> 2 #include <iostream> 3 #include <queue> 4 #include <cstring> 5 #include <algorithm> 6 #define N 200005 7 #define ll long long 8 using namespace std; 9 int a[100010]; 10 int b[100010]; 11 int c[2][400010]; 12 int n; 13 ll k; 14 void add(int x,int y,int f){ 15 for(;x<=400000;x+=(x&(-x))){ 16 c[f][x]+=y; 17 } 18 } 19 int query(int x,int f){ 20 int ret=0; 21 for(;x>0;x-=(x&(-x))){ 22 ret+=c[f][x]; 23 } 24 return ret; 25 } 26 bool check(int mid){ 27 ll ret=0; 28 int tmp=0; 29 memset(c,0,sizeof(c)); 30 add(N,1,0); 31 for(int i=1;i<=n;i++){ 32 if(a[i]>=mid) tmp++; 33 if(i&1){ 34 ret+=query(tmp*2-i+N,0); 35 add(tmp*2-i+N,1,1); 36 } 37 else{ 38 ret+=query(tmp*2-i+N,1); 39 add(tmp*2-i+N,1,0); 40 } 41 } 42 return ret>=k; 43 } 44 int main(){ 45 freopen("kth.in","r",stdin); 46 freopen("kth.out","w",stdout); 47 scanf("%d%lld",&n,&k); 48 for(int i=1;i<=n;i++){ 49 scanf("%d",&a[i]); 50 b[i]=a[i]; 51 } 52 sort(b+1,b+n+1); 53 int tmp=unique(b+1,b+n+1)-b-1; 54 for(int i=1;i<=n;i++){ 55 a[i]=lower_bound(b+1,b+tmp+1,a[i])-b; 56 } 57 int l=1; 58 int r=tmp; 59 while(l+1<r){ 60 int mid=l+r>>1; 61 if(check(mid)) l=mid; 62 else r=mid-1; 63 } 64 if(check(r)) cout<<b[r]; 65 else cout<<b[l]; 66 return 0; 67 } 68 /*--------------------- 69 作者:zhn_666 70 来源:CSDN 71 原文:https://blog.csdn.net/zhn_666/article/details/78236482 72 版权声明:本文为博主原创文章,转载请附上博文链接!*/
维护两个指针l,r
如果l~r这个区间的众数大于当前二分的值,那么[(1~l),r]都满足众数大于当前二分的值,
所以当一个区间满足时(从l到r,出现的数a[i],则s[a[i]]++,最后找最大的s[i])
l这个指针可以直接跳到r+1