2016多校10 HDU 5857 Median

11 篇文章 0 订阅
7 篇文章 0 订阅

2016多校联合训练#10

HDU 5857 Median

递归分治,亦可模拟

传送门:HDU


题意

给一个不递减的序列,给两个区间,求两个区间合并(不去重!!)后的中位数。

注意样例:

序列:1 2 3 4
区间:1 2
区间:2 4
结果是2.0


思路

模拟O(1)复杂度。。。不过我不会模拟。。分类讨论情况太多了。。随便膜一下灰模拟的大神>_<

然后说O(logn)的递归分治。这是算法导论上讲过的题。求两个序列第k大的数。首先有个很好理解但却不好想到的事情:两个有序序列AB(下标从1开始吧 简洁),求第k大,如果A[k/2]小于B[k/2],那么A[k/2]以前的数一定小于那个总体第k大的,这样找出了前k/2个数,每次k就减半了,递归下去就好。

再说一些细节问题:首先保证AB序列长度永远A小于B,如果A大于B,交换一下就好。然后是递归边界。如果A序列长度为0了,那么返回B序列第k大就好。如果k=0了,那”下一个”元素就是第k大,返回min(A[l1],B[l2])。(注意本题是一个序列的两段,所以返回min(l1,l2)即可,因为序列有序)。

最后,膜一下对我理解这题有帮助的blog。膜大佬a膜大佬b


代码

注意不加速的cin会T;输入有负数,读入挂没用

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <vector>
#include <queue>
#include <stack>
#include <iomanip>
#include <string>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

using namespace std;
const int MAXN=100005;
typedef double LL;
LL a[MAXN];


int find_median(int l1,int r1,int l2,int r2,int s)
{
    int m=r1-l1+1,n=r2-l2+1;
    if(m>n) return find_median(l2,r2,l1,r1,s);
    if(m==0) return l2+s-1;
    if(s==1)
    {
        return min(l1,l2);
    }
    int pa=min(s/2,m),pb=s-pa;
    if(a[l1+pa-1]<a[l2+pb-1]) return find_median(l1+pa,r1,l2,r2,s-pa);
    else if(a[l1+pa-1]>a[l2+pb-1]) return find_median(l1,r1,l2+pb,r2,s-pb);
    else return l1+pa-1;

}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int m,n;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%lf",&a[i]);
        }
        for(int i=0;i<m;i++)
        {
            int l1,l2,r1,r2;
            scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
            int k=r1-l1+r2-l2+2;
            if(k%2==1)
            {
                int res=find_median(l1,r1,l2,r2,k/2+1);
                printf("%.1lf\n",a[res]);
            }
            else
            {
                int resa=find_median(l1,r1,l2,r2,k/2+1);
                int resb=find_median(l1,r1,l2,r2,k/2);
                printf("%.1lf\n",(a[resa]+a[resb])*(0.5));
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值