2013-team4/code/partition-tree
从 Trac 迁移的文章
这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。
原文章内容如下:
{{{
划分树,用来查询静态数组区间第k大数,时间复杂度O(NlogN+NlogN+MlogN),分别是排序,建树,和查询的时间复杂度,空间复杂度O(NlogN)。
类似数据结构还有归并树,,,这个有待研究。
这到题目是UVA的1480.
}}}
{{{
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int MAXN=100000+10;
const int SIZE=25;
int A[MAXN], S[MAXN];//A是原序列, S是排序后的序列(为了减少变成复杂度,S表示的是下标)
int num[SIZE][MAXN], cnt[SIZE][MAXN];//cnt表示这一层,进入左子树的节点个数,num表示每层的节点
bool cmp(int i, int j) {return (A[i]<A[j]);}
void build(int l, int r, int d)
{
if (l==r) return;
int m=(l+r)>>1, p=0;
for (int i=l; i<=r; i++)
if (num[d][i]<=m)
{
num[d+1][l+p]=num[d][i];
cnt[d][i]=++p;
}
else
{
num[d+1][m+1+i-l-p]=num[d][i];
cnt[d][i]=p;
}
build(l, m, d+1); build(m+1, r, d+1);
}
int query(int l, int r, int d, int ll, int rr, int k)
{
if (l==r) return l;
int ls=(ll==l)?0:cnt[d][ll-1];
int rs=cnt[d][rr];
int m=(l+r)>>1;
if (k<=rs-ls) return query(l, m, d+1, l+ls, l+rs-1, k);
return query(m+1, r, d+1, m+1+(ll-l-ls), m+1+(rr-l-rs), k-(rs-ls));
}
struct node
{
int type, x, y, k;
}CMD[MAXN];
int main()
{
int N, M, Q, cas=0;
while (scanf("%d", &M)==1)
{
Q=N=0;
while (M--)
{
char st[30];
scanf("%s", st);
if (st[0]=='I')
{
N++; S[N]=N;
scanf("%d", &A[N]);
}
else
{
CMD[Q].type=st[6]-'1';
if (st[6]=='1') scanf("%d%d%d", &CMD[Q].x, &CMD[Q].y, &CMD[Q].k);
else if (st[6]=='2') scanf("%d", &CMD[Q].x), CMD[Q].y=N;
else if (st[6]=='3')
{
CMD[Q].x=1; CMD[Q].y=N;
scanf("%d", &CMD[Q].k);
}
Q++;
}
}
sort(S+1, S+1+N, cmp);
for (int i=1; i<=N; i++) num[0][S[i]]=i;
build(1, N, 0);
long long ret[3]={0, 0, 0};
for (int i=0; i<Q; i++)
{
int x=CMD[i].x, y=CMD[i].y, k=CMD[i].k;
if (CMD[i].type==0||CMD[i].type==2)
{
long long tmp=A[S[query(1, N, 0, x, y, k)]];
ret[CMD[i].type]+=tmp;
}
else
{
int l=1, r=y+1;
while (l<r)
{
int m=(l+r)>>1;
int ret=A[S[query(1, N, 0, 1, y, m)]];
if (ret>=x) r=m;
else l=m+1;
}
ret[1]+=r;
//printf("%d\n", r);
}
}
printf("Case %d:\n", ++cas);
for (int i=0; i<3; i++) printf("%lld\n", ret[i]);
}
return 0;
}
}}}
划分树,用来查询静态数组区间第k大数,时间复杂度O(NlogN+NlogN+MlogN),分别是排序,建树,和查询的时间复杂度,空间复杂度O(NlogN)。
类似数据结构还有归并树,,,这个有待研究。
这到题目是UVA的1480.
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int MAXN=100000+10;
const int SIZE=25;
int A[MAXN], S[MAXN];//A是原序列, S是排序后的序列(为了减少变成复杂度,S表示的是下标)
int num[SIZE][MAXN], cnt[SIZE][MAXN];//cnt表示这一层,进入左子树的节点个数,num表示每层的节点
bool cmp(int i, int j) {return (A[i]<A[j]);}
void build(int l, int r, int d)
{
if (l==r) return;
int m=(l+r)>>1, p=0;
for (int i=l; i<=r; i++)
if (num[d][i]<=m)
{
num[d+1][l+p]=num[d][i];
cnt[d][i]=++p;
}
else
{
num[d+1][m+1+i-l-p]=num[d][i];
cnt[d][i]=p;
}
build(l, m, d+1); build(m+1, r, d+1);
}
int query(int l, int r, int d, int ll, int rr, int k)
{
if (l==r) return l;
int ls=(ll==l)?0:cnt[d][ll-1];
int rs=cnt[d][rr];
int m=(l+r)>>1;
if (k<=rs-ls) return query(l, m, d+1, l+ls, l+rs-1, k);
return query(m+1, r, d+1, m+1+(ll-l-ls), m+1+(rr-l-rs), k-(rs-ls));
}
struct node
{
int type, x, y, k;
}CMD[MAXN];
int main()
{
int N, M, Q, cas=0;
while (scanf("%d", &M)==1)
{
Q=N=0;
while (M--)
{
char st[30];
scanf("%s", st);
if (st[0]=='I')
{
N++; S[N]=N;
scanf("%d", &A[N]);
}
else
{
CMD[Q].type=st[6]-'1';
if (st[6]=='1') scanf("%d%d%d", &CMD[Q].x, &CMD[Q].y, &CMD[Q].k);
else if (st[6]=='2') scanf("%d", &CMD[Q].x), CMD[Q].y=N;
else if (st[6]=='3')
{
CMD[Q].x=1; CMD[Q].y=N;
scanf("%d", &CMD[Q].k);
}
Q++;
}
}
sort(S+1, S+1+N, cmp);
for (int i=1; i<=N; i++) num[0][S[i]]=i;
build(1, N, 0);
long long ret[3]={0, 0, 0};
for (int i=0; i<Q; i++)
{
int x=CMD[i].x, y=CMD[i].y, k=CMD[i].k;
if (CMD[i].type==0||CMD[i].type==2)
{
long long tmp=A[S[query(1, N, 0, x, y, k)]];
ret[CMD[i].type]+=tmp;
}
else
{
int l=1, r=y+1;
while (l<r)
{
int m=(l+r)>>1;
int ret=A[S[query(1, N, 0, 1, y, m)]];
if (ret>=x) r=m;
else l=m+1;
}
ret[1]+=r;
//printf("%d\n", r);
}
}
printf("Case %d:\n", ++cas);
for (int i=0; i<3; i++) printf("%lld\n", ret[i]);
}
return 0;
}