在线段树中查询
Querying in segment tree
我一直在努力理解 query()
函数中最后 6 行的逻辑。
这是 spoj 上 GSS1 问题的代码。
解决方案link
#include <cstdio>
#include <algorithm>
#define MAX 70000
using namespace std;
struct no {
int lsum, rsum, msum;
};
int array[ MAX + 1 ], sums[ MAX + 1 ];
no tree[ 4 * MAX + 1 ];
void init( int node, int i, int j ) {
if ( i == j ) {
tree[ node ] = ( ( no ) { array[ i ], array[ i ], array[ i ] } );
}
else {
init( node * 2, i, ( i + j ) / 2 );
init( node * 2 + 1, ( i + j ) / 2 + 1, j );
no left = tree[ node * 2 ], right = tree[ node * 2 + 1 ];
tree[ node ].lsum = max( left.lsum, sums[ ( i + j ) / 2 ] - sums[ i - 1 ] + right.lsum );
tree[ node ].rsum = max( right.rsum, sums[ j ] - sums[ ( i + j ) / 2 ] + left.rsum );
tree[ node ].msum = max( left.msum, max( right.msum, left.rsum + right.lsum ) );
}}
no query( int node, int a, int b, int i, int j ) {
if ( a == i && b == j ) {
return tree[ node ];
}
else if ( j <= ( a + b ) / 2 ) {
return query( node * 2, a, ( a + b ) / 2, i, j );
}
if ( i > ( a + b ) / 2 ) {
return query( node * 2 + 1, ( a + b ) / 2 + 1, b, i, j );
}
no left = query( node * 2, a, ( a + b ) / 2, i, ( a + b ) / 2 );
no right = query( node * 2 + 1, ( a + b ) / 2 + 1, b, ( a + b ) / 2 + 1, j );
return ( ( no ) {
max( left.lsum, sums[ ( a + b ) / 2 ] - sums[ i - 1 ] + right.lsum ),
max( right.rsum, sums[ b ] - sums[ ( a + b ) / 2 ] + left.rsum ),
max( left.msum, max( right.msum, left.rsum + right.lsum ) )
} ); }
int main() {
int i, N, q, l, r;
scanf( "%d", &N );
for ( i = 0; i < N; ++i ) {
scanf( "%d", array + i );
if ( i == 0 ) {
sums[ i ] = array[ i ];
}
else {
sums[ i ] = sums[ i - 1 ] + array[ i ];
}
}
init( 1, 0, N - 1 );
scanf( "%d", &q );
for ( i = 0; i < q; ++i ) {
scanf( "%d%d", &l, &r );
--l;
--r;
printf( "%d\n", query( 1, 0, N - 1, l, r ).msum );
}
return 0; }
查询函数中那个no = left && no = right和那个return有什么用
谁能推荐更好的implementation/tutorial fr线段树
我在实现数据结构时无法想象这些递归。有什么建议吗?
What is the need of that no = left && no = right and that return in
query function
这就是您查询的段同时进入 children 的情况。
假设您有一个节点覆盖区间 1..n 并且有 2 children 1 .. n/2 和 n/2+1 ..n。如果你想查询某个区间 [a,b] 使得 a <=n/2 < b 那么你需要从左分支和右分支获取结果并将它们组合(换句话说将查询拆分为[a, n/2] 和 [n/2 +1, b] 得到 2 个结果并将它们合并。
为了提高效率,您可以证明对于任何查询都只能有一个这样的拆分,因为间隔现在已经触及边缘(因此,虽然您总是向下遍历 children 之一,但另一个是被忽略或完全落入查询中,你 return 在下一个递归步骤中)。
该代码是一个非常有效的实现(您不存储指向 children 的指针,节点范围是隐式的)。如果您正在尝试 learn/debug 寻找更明确地存储东西的东西,或者自己写一个。至少正确地缩进代码,将变量名更改为更易读的名称并将 (a+b)/2
替换为 middle
。这应该使代码更容易理解。还要在纸上画出结构(总是有帮助)。
我一直在努力理解 query()
函数中最后 6 行的逻辑。
这是 spoj 上 GSS1 问题的代码。
解决方案link
#include <cstdio>
#include <algorithm>
#define MAX 70000
using namespace std;
struct no {
int lsum, rsum, msum;
};
int array[ MAX + 1 ], sums[ MAX + 1 ];
no tree[ 4 * MAX + 1 ];
void init( int node, int i, int j ) {
if ( i == j ) {
tree[ node ] = ( ( no ) { array[ i ], array[ i ], array[ i ] } );
}
else {
init( node * 2, i, ( i + j ) / 2 );
init( node * 2 + 1, ( i + j ) / 2 + 1, j );
no left = tree[ node * 2 ], right = tree[ node * 2 + 1 ];
tree[ node ].lsum = max( left.lsum, sums[ ( i + j ) / 2 ] - sums[ i - 1 ] + right.lsum );
tree[ node ].rsum = max( right.rsum, sums[ j ] - sums[ ( i + j ) / 2 ] + left.rsum );
tree[ node ].msum = max( left.msum, max( right.msum, left.rsum + right.lsum ) );
}}
no query( int node, int a, int b, int i, int j ) {
if ( a == i && b == j ) {
return tree[ node ];
}
else if ( j <= ( a + b ) / 2 ) {
return query( node * 2, a, ( a + b ) / 2, i, j );
}
if ( i > ( a + b ) / 2 ) {
return query( node * 2 + 1, ( a + b ) / 2 + 1, b, i, j );
}
no left = query( node * 2, a, ( a + b ) / 2, i, ( a + b ) / 2 );
no right = query( node * 2 + 1, ( a + b ) / 2 + 1, b, ( a + b ) / 2 + 1, j );
return ( ( no ) {
max( left.lsum, sums[ ( a + b ) / 2 ] - sums[ i - 1 ] + right.lsum ),
max( right.rsum, sums[ b ] - sums[ ( a + b ) / 2 ] + left.rsum ),
max( left.msum, max( right.msum, left.rsum + right.lsum ) )
} ); }
int main() {
int i, N, q, l, r;
scanf( "%d", &N );
for ( i = 0; i < N; ++i ) {
scanf( "%d", array + i );
if ( i == 0 ) {
sums[ i ] = array[ i ];
}
else {
sums[ i ] = sums[ i - 1 ] + array[ i ];
}
}
init( 1, 0, N - 1 );
scanf( "%d", &q );
for ( i = 0; i < q; ++i ) {
scanf( "%d%d", &l, &r );
--l;
--r;
printf( "%d\n", query( 1, 0, N - 1, l, r ).msum );
}
return 0; }
查询函数中那个no = left && no = right和那个return有什么用
谁能推荐更好的implementation/tutorial fr线段树
我在实现数据结构时无法想象这些递归。有什么建议吗?
What is the need of that no = left && no = right and that return in query function
这就是您查询的段同时进入 children 的情况。
假设您有一个节点覆盖区间 1..n 并且有 2 children 1 .. n/2 和 n/2+1 ..n。如果你想查询某个区间 [a,b] 使得 a <=n/2 < b 那么你需要从左分支和右分支获取结果并将它们组合(换句话说将查询拆分为[a, n/2] 和 [n/2 +1, b] 得到 2 个结果并将它们合并。
为了提高效率,您可以证明对于任何查询都只能有一个这样的拆分,因为间隔现在已经触及边缘(因此,虽然您总是向下遍历 children 之一,但另一个是被忽略或完全落入查询中,你 return 在下一个递归步骤中)。
该代码是一个非常有效的实现(您不存储指向 children 的指针,节点范围是隐式的)。如果您正在尝试 learn/debug 寻找更明确地存储东西的东西,或者自己写一个。至少正确地缩进代码,将变量名更改为更易读的名称并将 (a+b)/2
替换为 middle
。这应该使代码更容易理解。还要在纸上画出结构(总是有帮助)。