51nod 1682 中位数计数【想法题】

题目:www.51nod.com/onlineJudge/questionCode.html#!problemId=1682

题意:

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

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

Input

第一行一个数n(n<=8000)
第二行n个数,0<=每个数<=10^9

Output

N个数,依次表示第i个数在多少包含其的区间中是中位数。

Input示例

5
1 2 3 4 5

Output示例

1 2 3 2 1

分析:

这题看错n的范围了,以为是6000(因为刚做了一个6000的题)….T_T…..
然后想也没想就以为是主席树模板题了,枚举每个区间,然后主席树找这个区间中位数。结果T了,时间复杂度是O(n^2logn),难道卡常数?
然后看了讨论,囧,原来看错数据范围了~~

正解是枚举每个数作为中位数x,那么这比这个数大的和比这个数小的数的数目是一样的。所以找这个中位数x所在的区间,那么对于这个数左边,统计>x和

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5;

int a[N], num[N], ans[N];

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    for (int i = 1; i <= n; i++) {
        int cnt = 0;
        memset(num, 0, sizeof(num));
        for (int j = i; j >= 1; j--) {
            if (a[j] > a[i]) ++cnt;
            if (a[j] < a[i]) --cnt;
            num[8000 + cnt]++;
        }
        cnt = 0;
        for (int j = i; j <= n; j++) {
            if (a[j] > a[i]) --cnt;
            if (a[j] < a[i]) ++cnt;
            ans[i] += num[8000 + cnt];
        }
    }
    for (int i = 1; i <= n; i++)
        printf("%d%c", ans[i], i == n ? '\n' : ' ');
    return 0;
}

T的代码:

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 1e4;

struct Node {
    int L, R, sum;
};
Node T[MAXN * 20];
int T_cnt;

void insert(int &num, int &x, int L, int R) {
    T[T_cnt++] = T[x];
    x = T_cnt - 1;
    ++T[x].sum;
    if(L == R) return ;
    int mid = (L + R) >> 1;
    if(num <= mid) insert(num, T[x].L, L, mid);
    else insert(num, T[x].R, mid + 1, R);
}

int query(int i, int j, int k, int L, int R) {
    if(L == R) return L;
    int t = T[T[j].L].sum - T[T[i].L].sum;
    int mid = (R + L) >> 1;
    if(k <= t) return query(T[i].L, T[j].L, k, L, mid);
    else return query(T[i].R, T[j].R, k - t, mid + 1, R);
}

struct A {
    int x, idx;
    bool operator < (const A &rhs) const {
        return x < rhs.x;
    }
};

A a[MAXN];
int rk[MAXN], root[MAXN],num[MAXN];
int n, m;

int main() {

    T[0].L = T[0].R = T[0].sum = 0;
    root[0] = 0;
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &a[i].x);
        a[i].idx = i;
    }
    sort(a + 1, a + n + 1);
    for(int i = 1; i <= n; ++i) rk[a[i].idx] = i;
    T_cnt = 1;
    for(int i = 1; i <= n; ++i) {
        root[i] = root[i - 1];
        insert(rk[i], root[i], 1, n);
    }

    for(int i=1; i<=n; i++) {
        num[a[i].idx]++;
        for(int j=i+1; j<=n; j++) {
            if((j-i+1)&1)
                num[a[query(root[i - 1], root[j], (j-i)/2+1, 1, n)].idx]++;
        }
    }
    for(int i=1; i<=n; i++)
        printf("%d ",num[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值