noip模拟 第K大区间【二分+树状数组】

定义一个长度为奇数的区间的值为其所包含的的元素的中位数。 现给出 n 个数,求将所有长度为奇数的区间的值排序后,第 K 大的值为多少。对于 80%的数据,1<=n<=1000 对于 100%的数据,1<=n<=100000, k<=奇数区间的数 

1.80pts 枚举起点和区间长度,二分+权值线段树求出每个区间中位数最后sort一下即可求出第K大 

 (附考场代码)

#include <bits/stdc++.h>
using namespace std;
int n,k,a[1000005],sz[5000005],b[1000005],ans;
int z[1000005],cnt;
struct node{int x,y,id;}c[1000005];
void update1(int rt,int l,int r,int pos){
if(l==r){sz[rt]--;return;}
int mid=(l+r)>>1;
if(pos<=mid)update1(rt<<1,l,mid,pos);
else update1(rt<<1|1,mid+1,r,pos);
sz[rt]=sz[rt<<1]+sz[rt<<1|1];
}
void update2(int rt,int l,int r,int pos){
if(l==r){sz[rt]++;return;}
int mid=(l+r)>>1;
if(pos<=mid)update2(rt<<1,l,mid,pos);
else update2(rt<<1|1,mid+1,r,pos);
sz[rt]=sz[rt<<1]+sz[rt<<1|1];
}
int query(int rt,int l,int r,int lpos,int rpos){
if(lpos>r||rpos<l)return 0;
if(lpos<=l&&rpos>=r)return sz[rt];
int mid=(l+r)>>1;
    return query(rt<<1,l,mid,lpos,rpos)+query(rt<<1|1,mid+1,r,lpos,rpos);
}
int solve(int l,int r,int t){
  int res;
  while(l<=r){
    int mid=(l+r)>>1;
   if(query(1,1,n,1,mid)>=t)res=mid,r=mid-1;
   else l=mid+1;
  }return res;
}
bool cmp(int a,int b){return a>b;}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){scanf("%d",&c[i].y);b[i]=c[i].y;c[i].id=i;}
sort(b+1,b+1+n);
for(int i=1;i<=n;i++){c[i].x=lower_bound(b+1,b+1+n,c[i].y)-b;}
for(int i=1;i<=n;i++)z[++cnt]=c[i].x;
for(int i=1;i<=n;i++){
update2(1,1,n,c[i].x);
for(int j=3;i+j-1<=n;j+=2){
  update2(1,1,n,c[i+j-1].x);update2(1,1,n,c[i+j-2].x);
  z[++cnt]=solve(1,n,j/2+1);
}update1(1,1,n,c[i].x);
for(int j=3;i+j-1<=n;j+=2){
   update1(1,1,n,c[i+j-1].x);update1(1,1,n,c[i+j-2].x);
}
}
sort(z+1,z+1+cnt,cmp);
for(int i=1;i<=n;i++){if(c[i].x==z[k])ans=c[i].y;}
printf("%d\n",ans);
return 0;
}

2.100pts

 二分答案 t,统计中位数大于等于 t 的区间有多少个。 设 a[i]为前 i 个数中有 a[i]个数>=t,若奇数区间[l,r]的中位数>=t,则(a[r]-a[l-1])*2>r-l+1,即 (a[r]*2-r)>(a[l-1]*2-l+1)。 设 b[i]=a[i]*2-i,统计每个 b[i]有多少个 b[j]<b[i](j<i且奇偶性不同)

总复杂度 O(nlognlogn)

(附ac代码)

#include <bits/stdc++.h>
using namespace std;
int c1[500005],c2[500005],n,k,a[500005],sum[500005],b[500005];
int lowbit(int x){return x&(-x);} 
void update1(int x){while(x<=2*n){c1[x]++;x+=lowbit(x);}}
int query1(int x){int res=0;while(x){res+=c1[x];x-=lowbit(x);}return res;}
void update2(int x){while(x<=2*n){c2[x]++;x+=lowbit(x);}}
int query2(int x){int res=0;while(x){res+=c2[x];x-=lowbit(x);}return res;}
bool check(int mid){
	memset(c1,0,sizeof(c1));memset(c2,0,sizeof(c2));int tmp=0;
	for(int i=1;i<=n;i++)sum[i]=sum[i-1]+(a[i]>=mid);
	for(int i=1;i<=n;i++)sum[i]=2*sum[i]-i+n;
	update2(n);
	for(int i=1;i<=n;i++){
		if(i&1)tmp+=query2(sum[i]-1),update1(sum[i]);
		else tmp+=query1(sum[i]-1),update2(sum[i]);
	}return tmp>=k;
}
int main(){
	scanf("%d%d",&n,&k);int l=1,r=n,ans;
	for(int i=1;i<=n;i++)scanf("%d",&a[i]),b[i]=a[i];
	sort(b+1,b+1+n);
	while(l<=r){
		int mid=(l+r)>>1;
		if(check(b[mid]))ans=mid,l=mid+1;
		else r=mid-1;
	}
	printf("%d\n",b[ans]);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值