【发布时间】: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 年后它会成为家庭作业 :-)