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;
}