中位数计数 51Nod - 1682 (思路题,详解)

https://cn.vjudge.net/problem/51Nod-1682

中位数定义为所有值从小到大排序后排在正中间的那个数,如果值有偶数个,通常取最中间的两个数值的平均数作为中位数。

现在有n个数,每个数都是独一无二的,求出每个数在多少个包含其的区间中是中位数。


Input第一行一个数n(n<=8000) 
第二行n个数,0<=每个数<=10^9OutputN个数,依次表示第i个数在多少包含其的区间中是中位数。Sample Input
5
1 2 3 4 5
Sample Output
1 2 3 2 1

题意:中文题意就不解释了。

思路:先将数字编号,然后带着编号进行结构体排序。

例如:13

x:   5  8  7  11 2 13  3  6  12  9   4   1  10(这些数字范围很大,我们不需要管,只需要按照x排个序即可)
id:  1  2  3  4  5  6  7   8  9   10 11 12 13
x:   1  2  3  4  5  6  7   8  9   10 11 12 13

id:12  5  7 11  1  8  3  2  10  13  4   9   6

先从x小的数字算起,从小开始遍历到大;

int b[N];  b[i]表示第i个位置是否已经出现过,出现过的为1,正在出现为0,未出现则为-1;mem(b,-1); 方便初始化。(后面的操作针对的都是位置,排序之后的数字相对大小用b数组的值来确定)

现在开始从最小的数字遍历起,最小的数字在12这个位置

 1   2  3  4   5   6  7  8  9  10  11 12  13
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1  -1   0   -1 

现在只有12这个位置出现了。我先不算位置12的数字为中位数怎么求。(先理解b数组,0,1,-1)

现在位置5的数字出现了,位置12的数字出现过了

 1   2  3  4   5   6  7  8  9  10  11 12  13

-1 -1 -1 -1  0  -1 -1 -1 -1 -1  -1    1   -1 

出现过的位置写成1,没出现的位置还是-1,正在计算的位置是0

中间的过程就不详细写了,直接跳到位置8出现的时候(12,5,7,11,1这些位置都已经出现过了)

 1   2  3  4   5   6  7  8  9  10  11 12  13

 1 -1 -1 -1   1 -1   1  0 -1 -1    1   1   -1 

现在计算位置8上的数字为中位数的区间个数:首先区间一定要包含8这个位置,并且区间是连续的,8这个位置上的数字为中位数。首先想怎么保证所选区间,8这个位置上的数字为中位数,比这个位置小的数字和比这个位置大的数字一样多即可。

比这个位置小的数字的表现是什么,就是当前b数组值为1,大的呢,值为-1,那么一样多又怎么表示呢,那不就是1的个数和-1的个数一样多。前提是在一个包含位置8的区间,并且1和-1的个数一样多。那么你求出b数组的前n项和,再看看。

   1   2  3  4   5   6  7         8  9  10  11 12  13

   1 -1 -1 -1   1 -1   1        0 -1  -1   1   1   -1 

0 1  0  -1 -2  -1 -2 -1      -1 -2 -3  -2 -1  -2

现在把这些数字分成两部分,后面的-2和前面的-2,是相等的,那么用后面的减去前面的值不就为0吗。0又代表什么呢。

就比如说两个带下划线的两个-2,用后面的减去前面的,那么就表示的是【5,13】这个区间,这个区间符合题意吗,首先满足包含位置8,并且是一个区间。那么再结合区间的b数组之和为0,思考位置8的数字是否为中位数。因为区间的b数组之和为0,那么1的个数,和-1的个数一样多,是1则表示小于位置8的数字,-1则表示大于数字8的位置。那么这样的区间是符合的。那么只需要统计一下8后面(包含8的位置)的数字在前面都出现了几次,不就行了。比如位置8的-1,-1在前面出现了3次,那么说明以8为右区间端点的满足题意的区间有3个(【4,8】【6,8】【8,8】),位置12下面的-1也表示有同样的3个(4【4,12】【6,12】【8,12】),就这样用桶排序存一下位置8前面出现过多少个这样的数字即可。不要忘了b数组前0项和为0哦。

(注意:桶排序的数字是有负数的,所以将所有要桶排序的数字加上一个8000,就一定不会是负数了)

每个数字只需要计算一次o(n),所以总复杂度n^2.

无力吐槽:比赛时还是没太理清楚思路,把位置和数字混淆了,导致多加了个限制条件(画蛇添足),可惜可惜。比赛时的代码删了几行就AC了。QAQ

代码:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <stdlib.h>
#include <string>
#include <map>
#include <iostream>
#include <sstream>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <algorithm>
#include<list>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define inf 0x3f3f3f3f
typedef long long ll;
const int N=16000+10;//桶排数组要开成双倍
struct node
{
    int x,id;
} a[N];
struct hpc
{
    bool operator()(node a,node b)
    {
        return a.x<b.x;
    }
};
int b[N];
int c[N];
int ans[N];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&a[i].x);
        a[i].id=i;
    }
    sort(a+1,a+1+n,hpc());
    mem(b,-1);//初始化为-1,表示未出现过的位置
    mem(ans,0);//存答案
    for(int i=1; i<=n; i++)//从小到大遍历数组
    {
        int id=a[i].id;//当前数字所在的位置
        b[id]=0;//当前的位置赋值为0
        int sum=8000;//统计b数组的前n项和
        mem(c,0);//桶排序的数组初始化
        c[8000]=1;//最开始的sum也算出现了1次
        for(int j=1; j<id; j++)//前面桶排出现过的sum
        {
            sum+=b[j];
            c[sum]++;
        }
        for(int j=id; j<=n; j++)//后面使用前面桶排好的c数组
        {
            sum+=b[j];
            ans[id]+=c[sum];//这答案是位置id的答案。
        }
        b[id]=1;//计算过后将表示变为1;
    }
    for(int i=1; i<=n; i++)
    {
        if(i!=1) printf(" ");
        printf("%d",ans[i]);
    }
    printf("\n");
    return 0;
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值