【问题标题】:A data structure problem数据结构问题
【发布时间】:2011-03-19 13:25:49
【问题描述】:

给定一个整数序列,有许多查询。 每个查询都有一个范围 [l, r],你要找到给定范围 [l, r] 的中位数

查询数量可高达 100,000 序列的长度可以大到100,000

不知道有没有什么数据结构可以支持这样的查询


我的解决方案:

我今天咨询了我的伙伴,他告诉我要使用分区树。

我们可以在 nlog(n) 时间内构建一个分区树,并在 log(n) 时间内回答每个查询

分区树其实就是归并排序的过程,但是对于树中的每个节点,它保存的是去左子树的整数个数。因此,我们可以使用这些信息来处理查询。

这是我的代码:

这个程序是在给定的区间 [l, r] 中找到 x,使下面的方程最小化。

alt text http://acm.tju.edu.cn/toj/3556_01.jpg

说明:

seq 保存序列

pos保存排序后的位置

ind 保存索引

cntL 保存给定范围内去左树的整数个数

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 100008
typedef long long LL;
int n, m, seq[N], ind[N], pos[N], next[N];
int cntL[20][N];
LL sum[20][N], sumL, subSum[N];

void build(int l, int r, int head, int dep)
{
    if (l == r)
    {
        cntL[dep][l] = cntL[dep][l-1];
        sum[dep][l] = sum[dep][l-1];
        return ;
    }
    int mid = (l+r)>>1;
    int hl = 0, hr = 0, tl = 0, tr = 0;
    for (int i = head, j = l; i != -1; i = next[i], j++)
    {
        cntL[dep][j] = cntL[dep][j-1];
        sum[dep][j] = sum[dep][j-1];
        if (pos[i] <= mid)
        {
            next[tl] = i;
            tl = i;
            if (hl == 0) hl = i;
            cntL[dep][j]++;
            sum[dep][j] += seq[i];
        }
        else
        {
            next[tr] = i;
            tr = i;
            if (hr == 0) hr = i;
        }
    }
    next[tl] = -1;
    next[tr] = -1;
    build(l, mid, hl, dep+1);
    build(mid+1, r, hr, dep+1);
}

int query(int left, int right, int ql, int qr, int kth, int dep)
{
    if (left == right)
    {
        return ind[left];
    }
    int mid = (left+right)>>1;
    if (cntL[dep][qr] - cntL[dep][ql-1] >= kth)
    {
        return query(left, mid, left+cntL[dep][ql-1]-cntL[dep][left-1], left+cntL[dep][qr]-cntL[dep][left-1]-1, kth, dep+1);
    }
    else
    {
        sumL += sum[dep][qr]-sum[dep][ql-1];
        return query(mid+1, right, mid+1+ql-left-(cntL[dep][ql-1]-cntL[dep][left-1]), mid+qr+1-left-(cntL[dep][qr]-cntL[dep][left-1]), \
                kth-(cntL[dep][qr]-cntL[dep][ql-1]), dep+1);
    }
}

inline int cmp(int x, int y)
{
    return seq[x] < seq[y];
}

int main()
{
    int ca, t, i, j, middle, ql, qr, id, tot;
    LL ans;
    scanf("%d", &ca);
    for (t = 1; t <= ca; t++)
    {
        scanf("%d", &n);
        subSum[0] = 0;
        for (i = 1; i <= n; i++) 
        {
            scanf("%d", seq+i);
            ind[i] = i;
            subSum[i] = subSum[i-1]+seq[i];
        }
        sort(ind+1, ind+1+n, cmp);
        for (i = 1; i <= n; i++)
        {
            pos[ind[i]] = i;
            next[i] = i+1;
        }
        next[n] = -1;
        build(1, n, 1, 0);
        printf("Case #%d:\n", t);
        scanf("%d", &m);
        while (m--)
        {
            scanf("%d%d", &ql, &qr);
            ql++, qr++;
            middle = (qr-ql+2)/2;
            sumL= 0;
            id = query(1, n, ql, qr, middle, 0);
            ans = subSum[qr]-subSum[ql-1]-sumL;
            tot = qr-ql+1;
            ans = ans-(tot-middle+1)*1ll*seq[id]+(middle-1)*1ll*seq[id]-sumL;
            printf("%lld\n", ans);
        }
        puts("");
    }
}

【问题讨论】:

  • 删除了作业标签。家庭作业似乎太难了。
  • @Moron:虽然我同意,但这取决于作为家庭作业的位置。 :-)
  • @Shree:也许在另一个星球上:-P
  • @Moron:哎呀,对不起。
  • @Bolt:听起来很像家庭作业,你只是想帮忙,所以别担心 :-) 无论如何,也许 10 年后它会成为家庭作业 :-)

标签: algorithm data-structures


【解决方案1】:

这称为范围中位数查询问题。以下论文可能是相关的:Towards Optimal Range Medians。 (免费链接,感谢 belisarius)。

来自论文摘要:

我们考虑以下问题: 给定一个包含 n 个元素的未排序数组, 和一系列区间 数组,计算每个中的中位数 由定义的子数组 间隔。我们描述一个简单的 需要 O(nlogk+klogn) 的算法 是时候回答 k 个这样的中值查询了。 这将以前的算法改进了一个 对数因子和匹配 k=O(n) 的比较下限。这 我们简单的空间复杂度 算法在指针中是 O(nlogn) 机器模型,以及 RAM 中的 O(n) 模型。在后一个模型中,一个更 所涉及的 O(n) 空间数据结构可以 在 O(nlogn) 时间内构建,其中 每个查询的时间减少到 O(logn/loglogn)。我们也给 两者的有效动态变体 数据结构,实现 O(log^2n) 使用 O(nlogn) 空间的查询时间 比较模型和 O((logn/loglogn)^2) 查询时间使用 RAM 中的 O(nlogn/loglogn) 空间 模型,并表明在细胞探针 模型,任何数据结构 支持 O(log^O(1)n) 时间的更新 必须有 Ω(logn/loglogn) 查询时间。

我们的方法自然地推广到 高维范围中位数 问题,其中元素位置和 查询范围是多维的——它 将范围中值查询减少到 范围计数的对数 查询。

当然,您可以在 O(n^3) 时间(或者甚至 O(n^2logn) 时间)和 O(n^2) 空间内预处理整个数组,以便能够返回 O( 1) 时间。

额外的约束可能有助于简化解决方案。例如,我们是否知道 r-l 会小于已知常数?等等……

【讨论】:

    猜你喜欢
    • 2013-01-20
    • 1970-01-01
    • 1970-01-01
    • 2015-09-03
    • 2011-05-14
    • 1970-01-01
    相关资源
    最近更新 更多