2.第k大区间
(kth.cpp/c/pas)
时间限制:1s
内存限制:256MB
【问题描述】
定义一个长度为奇数的区间的值为其所包含的的元素的中位数。
现给出n个数,求将所有长度为奇数的区间的值排序后,第K大的值为多少。
【输入】
输入文件名为kth.in。
第一行两个数n和k
第二行,n个数。(0<=每个数<231)
【输出】
输出文件名为kth.out。
一个数表示答案。
【输入输出样例】
kth.in kth.out
4 3
3 1 2 4 2
【样例解释】
[l,r]表示区间l~r的值
[1,1]:3
[2,2]:1
[3,3]:2
[4,4]:4
[1,3]:2
[2,4]:2
【数据说明】
对于30%的数据,1<=n<=100;
对于60%的数据,1<=n<=300
对于80%的数据,1<=n<=1000
对于100%的数据,1<=n<=100000, k<=奇数区间的数
考试的时候除了暴力啥也没有想到
下面时考试代码
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define N 100005
#define M 10005
using namespace std;
int n,k;
int a[100016];
int ans[6016];
int tem[6015];
int b[100016];
int f[100016][5];
int maxn=0;
int l,r;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
inline bool cmp(int a,int b){
return a>b;
}
inline int lowbit(int x){
return x&(-x);
}
inline int gsum(int x,int p){
int s=0;
for(register int i=x;i>0;i-=lowbit(i)) s+=f[i][p];
return s;
}
inline void update(int x,int p){
if(!x) return ;
for(register int i=x;i<N;i+=lowbit(i)) f[i][p]+=1;
}
inline int cha(int val){
memset(f,0,sizeof(f));
b[0]=0;
for(register int i=1;i<=n;i++){
if(a[i]>=val) b[i]=1;
else b[i]=0;
b[i]+=b[i-1];
}
for(register int i=1;i<=n;i++) b[i]=b[i]*2-i+M;
int s=0;
update(M,0);
for(register int i=1;i<=n;i++){
s+=gsum(b[i],(i&1)^1);
update(b[i],(i&1));
}
return s;
}
int main(){
freopen("kth.in","r",stdin);
freopen("kth.out","w",stdout);
n=read();
k=read();
for(register int i=1;i<=n;++i){
a[i]=read();
if(maxn<a[i]) maxn=a[i];
}
if(n<=1000){
//60分 ~80分
int lm;
if(n&1) lm=n-1;
else lm=n;
int tot=0;
for(register int i=1;i<=n;++i){
ans[++tot]=a[i];
}
for(register int i=3;i<=lm;i+=2){
//枚举区间长度
for(register int j=1;j<=n-i+1;++j){
//枚举起点嘿嘿嘿
int zhong=j+i-1;
for(register int k=1;k<=n;++k){
tem[k]=a[k];
}
sort(tem+j,tem+zhong+1);
ans[++tot]=tem[(j+zhong)/2];
}
}
sort(ans+1,ans+tot+1,cmp);
printf("%d",ans[k]);
return 0;
}
int l=0,r=maxn,ans=0;
while(l<=r){
//乱写二分
int mid=(l+r)>>1;
if(cha(mid)>=k) l=mid+1,ans=mid;
else r=mid-1;
}
printf("%d\n",ans);
return 0;
}
这是标程
#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
#include <algorithm>
#define N 200005
#define ll long long
using namespace std;
int a[100010];
int b[100010];
int c[2][400010];
int n;
ll k;
void add(int x,int y,int f){
for(;x<=400000;x+=(x&(-x))){
c[f][x]+=y;
}
}
int query(int x,int f){
int ret=0;
for(;x>0;x-=(x&(-x))){
ret+=c[f][x];
}
return ret;
}
bool check(int mid){
ll ret=0;
int tmp=0;
memset(c,0,sizeof(c));
add(N,1,0);
for(int i=1;i<=n;i++){
if(a[i]>=mid) tmp++;
if(i&1){
ret+=query(tmp*2-i+N,0);
add(tmp*2-i+N,1,1);
}
else{
ret+=query(tmp*2-i+N,1);
add(tmp*2-i+N,1,0);
}
}
return ret>=k;
}
int main(){
freopen("kth.in","r",stdin);
freopen("kth.out","w",stdout);
scanf("%d%lld",&n,&k);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(b+1,b+n+1);
int tmp=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;i++){
a[i]=lower_bound(b+1,b+tmp+1,a[i])-b;
}
int l=1;
int r=tmp;
while(l+1<r){
int mid=l+r>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
if(check(r)) cout<<b[r];
else cout<<b[l];
return 0;
}
主要呢,这题学会了离散化
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(b+1,b+n+1);
int tmp=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;i++){
a[i]=lower_bound(b+1,b+tmp+1,a[i])-b;
}
unique(b+1,b+n+1)去重,并返回去重后的最后一个元素的后一位的指针,减去b(首指针),然后再减1就是去重后的个数
a[i]=lower_bound(b+1,b+tmp+1,a[i])-b;
lower_bound(b+1,b+tmp+1,a[i])返回的是a[i]在b中的位置,减b,就没了
总结
①离散化真好写
②duan2 太强了