https://cn.vjudge.net/problem/51Nod-1682
中位数定义为所有值从小到大排序后排在正中间的那个数,如果值有偶数个,通常取最中间的两个数值的平均数作为中位数。
现在有n个数,每个数都是独一无二的,求出每个数在多少个包含其的区间中是中位数。
Input第一行一个数n(n<=8000)
第二行n个数,0<=每个数<=10^9OutputN个数,依次表示第i个数在多少包含其的区间中是中位数。Sample Input
5 1 2 3 4 5Sample 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;
}