POJ_2104

    上课之前大概看了一下划分树,后来上课的时候自己YY了一个“划分树”,结果发现每次查询复杂度不是logn的,大约是logn*logn的,写出来之后一交好像跟归并树的复杂度差不多,也就这个就是归并树吧……

    今天不早了,就先睡了,等明天起来之后再仔细研究一下划分树怎么写吧。

View Code
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define MAXD 400010
#define MAXM 2000010
int N, M, a[MAXD], x[MAXM], p, left[MAXD], right[MAXD], lx[MAXD], rx[MAXD], e;
int cmpr(const void *_p, const void *_q)
{
int *p = (int *)_p, *q = (int *)_q;
if(a[*p] == a[*q])
return *p - *q;
return a[*p] - a[*q];
}
int cmp(const void *_p, const void *_q)
{
int *p = (int *)_p, *q = (int *)_q;
return *p - *q;
}
void init()
{
int i;
for(i = 1; i <= N; i ++)
{
scanf("%d", &a[i]);
x[i] = i;
}
}
void add(int l, int r)
{
int i;
++ e;
left[e] = right[e] = -1;
lx[e] = p, rx[e] = p + r - l;
for(i = l; i <= r; i ++)
x[p ++] = x[i];
}
void build(int cur)
{
int l, r, m;
l = lx[cur], r = rx[cur];
if(l != r)
{
m = (l + r) / 2;
add(l, m), left[cur] = e;
build(left[cur]);
if(m < r)
{
add(m + 1, r), right[cur] = e;
build(right[cur]);
}
qsort(&x[l], r - l + 1, sizeof(x[0]), cmp);
}
}
int search(int l, int r, int k, int cur)
{
if(left[cur] == -1)
return a[x[lx[cur]]];
int i, j, p, n, min, mid, max;
p = left[cur];
if(x[rx[p]] < l || x[lx[p]] > r)
n = 0;
else
{
min = lx[p], max = rx[p] + 1;
for(;;)
{
mid = (min + max) / 2;
if(mid == min)
break;
if(x[mid] < l)
min = mid;
else
max = mid;
}
j = x[mid] < l ? max : mid;
min = lx[p], max = rx[p] + 1;
for(;;)
{
mid = (min + max) / 2;
if(mid == min)
break;
if(x[mid] <= r)
min = mid;
else
max = mid;
}
n = max - j;
}
if(n >= k)
return search(l, r, k, left[cur]);
else
return search(l, r, k - n, right[cur]);
}
void solve()
{
int i, j, k, l, r;
e = 0;
left[e] = right[e] = -1;
lx[e] = 1, rx[e] = N;
qsort(&x[1], N, sizeof(x[0]), cmpr);
p = N + 1;
build(0);
for(i = 0; i < M; i ++)
{
scanf("%d%d%d", &l, &r, &k);
printf("%d\n", search(l, r, k, 0));
}
}
int main()
{
while(scanf("%d%d", &N, &M) == 2)
{
init();
solve();
}
return 0;
}

    上完体育课后仔细学了一下这个博客(http://blog.csdn.net/jasonzhu8/article/details/5833339)里面划分树的写法,发现实际上我昨天YY的那个已经很像划分树了,只不过在建树和查找的时候做得过于繁琐了。这个博客里里面划分树上并没有存实际的元素,也没有存元素的标号,而是存的该元素在原序列中的rank,这样能够在建树的过程中能够很方便地统计出每个节点第i个元素及之前有多少元素划分到了左子树中,原因就在于每个节点上各元素的rank一定会组成一个连续的区间,而且区间的范围就等于数组下标的范围,这样只要通过将区间的中点mid找到,rank小于或等于mid的元素就会被划到左子树中。

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define MAXK 20
#define MAXD 100010
int N, M, sa[MAXD], rank[MAXK][MAXD], sum[MAXK][MAXD], a[MAXD];
int cmp(const void *_p, const void *_q)
{
int *p = (int *)_p, *q = (int *)_q;
if(a[*p] == a[*q])
return *p - *q;
return a[*p] - a[*q];
}
void init()
{
int i, j, k;
for(i = 1; i <= N; i ++)
{
scanf("%d", &a[i]);
sa[i] = i;
}
}
void build(int lx, int rx, int d)
{
if(lx == rx)
return ;
int i, j, k, mid = (lx + rx) / 2, p = 0;
for(i = lx; i <= rx; i ++)
{
if(rank[d][i] <= mid)
{
rank[d + 1][lx + p] = rank[d][i];
++ p;
}
else
rank[d + 1][mid + i - lx + 1 - p] = rank[d][i];
sum[d][i] = p;
}
build(lx, mid, d + 1);
build(mid + 1, rx, d + 1);
}
int search(int lx, int rx, int x, int y, int k, int d)
{
if(lx == rx)
return a[sa[rank[d][lx]]];
int n, m, mid = (lx + rx) / 2;
n = sum[d][y], m = (x == lx ? 0 : sum[d][x - 1]);
if(n - m >= k)
return search(lx, mid, lx + m, lx + n - 1, k, d + 1);
else
return search(mid + 1, rx, mid + 1 + x - lx - m, mid + 1 + y - lx - n, k - n + m, d + 1);
}
void solve()
{
int i, j, k, x, y;
qsort(sa + 1, N, sizeof(sa[0]), cmp);
for(i = 1; i <= N; i ++)
rank[0][sa[i]] = i;
build(1, N, 0);
for(i = 0; i < M; i ++)
{
scanf("%d%d%d", &x, &y, &k);
printf("%d\n", search(1, N, x, y, k, 0));
}
}
int main()
{
while(scanf("%d%d", &N, &M) == 2)
{
init();
solve();
}
return 0;
}



posted on 2012-03-14 00:30  Staginner  阅读(249)  评论(0编辑  收藏  举报