这是我对这种数据结构和一些模板戏法的尝试。
在所有这些混乱的底部,有两个平面数组的访问,其中一个包含总和树,另一个包含要向下传播的进位值树。在概念上,它们形成一个二叉树。
二叉树中节点的真实值是存储的总和树中的值加上节点下叶子数乘以从节点返回到根的所有进位树值的总和。
同时,树中每个节点的真实值等于其下叶子节点的真实值。
我编写了一个函数来处理进位和总和,因为它们访问相同的节点。有时读取会写入。因此,通过使用零的“增量”来调用它来获得总和。
所有模板戏法所做的就是计算每个树节点的偏移量以及左右子节点的位置。
虽然我使用了一个结构体,但该结构体是瞬态的——它只是一个包装器,具有指向数组偏移量的一些预计算值。我确实存储了指向数组开头的指针,但是在程序中,每个block_ptr都使用完全相同的root值。
为了调试,我有一些可怜的Assert()和Debug()宏,以及递归求和函数调用的跟踪无参函数(我用它来跟踪调用总数)。再次,为了避免全局状态而不必要地复杂。 :)
#include <memory>
#include <iostream>
enum {max_tier = 30};
typedef long long intt;
#define Assert(x) (!(x)?(std::cout << "ASSERT FAILED: (" << #x << ")\n"):(void*)0)
#define DEBUG(x)
template<size_t tier, size_t count=0>
struct block_ptr
{
enum {array_size = 1+block_ptr<tier-1>::array_size * 2};
enum {range_size = block_ptr<tier-1>::range_size * 2};
intt* root;
size_t offset;
size_t logical_offset;
explicit block_ptr( intt* start, size_t index, size_t logical_loc=0 ):root(start),offset(index), logical_offset(logical_loc) {}
intt& operator()() const
{
return root[offset];
}
block_ptr<tier-1> left() const
{
return block_ptr<tier-1>(root, offset+1, logical_offset);
}
block_ptr<tier-1> right() const
{
return block_ptr<tier-1>(root, offset+1+block_ptr<tier-1>::array_size, logical_offset+block_ptr<tier-1>::range_size);
}
enum {is_leaf=false};
};
template<>
struct block_ptr<0>
{
enum {array_size = 1};
enum {range_size = 1};
enum {is_leaf=true};
intt* root;
size_t offset;
size_t logical_offset;
explicit block_ptr( intt* start, size_t index, size_t logical_loc=0 ):root(start),offset(index), logical_offset(logical_loc)
{}
intt& operator()() const
{
return root[offset];
}
block_ptr<0> left() const { Assert(false); return *this; }
block_ptr<0> right() const { Assert(false); return *this; }
};
template<size_t tier>
void propogate_carry( block_ptr<tier> values, block_ptr<tier> carry )
{
if (carry() != 0)
{
values() += carry() * block_ptr<tier>::range_size;
if (!block_ptr<tier>::is_leaf)
{
carry.left()() += carry();
carry.right()() += carry();
}
carry() = 0;
}
}
template<size_t tier, typename trace>
intt query_or_modify( block_ptr<tier> values, block_ptr<tier> carry, int begin, int end, int increase=0, trace const& tr = [](){} )
{
tr();
DEBUG(
std::cout << begin << " " << end << " " << increase << "\n";
if (increase)
{
std::cout << "Increasing " << end-begin << " elements by " << increase << " starting at " << begin+values.offset << "\n";
}
else
{
std::cout << "Totaling " << end-begin << " elements starting at " << begin+values.logical_offset << "\n";
}
)
if (end <= begin)
return 0;
size_t mid = block_ptr<tier>::range_size / 2;
DEBUG( std::cout << "[" << values.logical_offset << ";" << values.logical_offset+mid << ";" << values.logical_offset+block_ptr<tier>::range_size << "]\n"; )
bool bExact = (begin == 0 && end >= block_ptr<tier>::range_size);
if (block_ptr<tier>::is_leaf)
{
Assert(bExact);
}
bExact = bExact || block_ptr<tier>::is_leaf;
if (bExact)
{
carry()+=increase;
intt retval = (values()+carry()*block_ptr<tier>::range_size);
DEBUG( std::cout << "Exact sum is " << retval << "\n"; )
return retval;
}
propogate_carry(values, carry);
values() += increase * end-begin;
if (begin >= mid)
{
DEBUG( std::cout << "Right:"; )
intt retval = query_or_modify( values.right(), carry.right(), begin-mid, end-mid, increase, tr );
DEBUG( std::cout << "Right sum is " << retval << "\n"; )
return retval;
}
else if (end <= mid)
{
DEBUG( std::cout << "Left:"; )
intt retval = query_or_modify( values.left(), carry.left(), begin, end, increase, tr );
DEBUG( std::cout << "Left sum is " << retval << "\n"; )
return retval;
}
else
{
DEBUG( std::cout << "Left:"; )
intt left = query_or_modify( values.left(), carry.left(), begin, mid, increase, tr );
DEBUG( std::cout << "Right:"; )
intt right = query_or_modify( values.right(), carry.right(), 0, end-mid, increase, tr );
DEBUG( std::cout << "Right sum is " << left << " and left sum is " << right << "\n"; )
return left+right;
}
}
以下是翻译的结果:
这里提供一些辅助类以便更轻松地创建指定大小的线段树。但需要注意的是,你只需要一个正确大小的数组,并且可以通过指向元素0的指针构造一个block_ptr,就可以开始使用了。
template<size_t tier>
struct segment_tree
{
typedef block_ptr<tier> full_block_ptr;
intt block[full_block_ptr::range_size];
full_block_ptr root() { return full_block_ptr(&block[0],0); }
void init()
{
std::fill_n( &block[0], size_t(full_block_ptr::range_size), 0 );
}
};
template<size_t entries, size_t starting=0>
struct required_tier
{
enum{ tier =
block_ptr<starting>::array_size >= entries
?starting
:required_tier<entries, starting+1>::tier
};
enum{ error =
block_ptr<starting>::array_size >= entries
?false
:required_tier<entries, starting+1>::error
};
};
template<size_t entries>
struct required_tier<entries, size_t(max_tier)>
{
enum{ tier = 0 };
enum{ error = true };
};
typedef required_tier< 1000000 > how_big;
enum {tier = how_big::tier};
int main()
{
segment_tree<tier> values;
segment_tree<tier> increments;
Assert(!how_big::error);
values.init();
increments.init();
auto value_root = values.root();
auto carry_root = increments.root();
size_t count = 0;
auto tracer = [&count](){count++;};
intt zero = query_or_modify( value_root, carry_root, 0, 100000, 0, tracer );
std::cout << "zero is " << zero << " in " << count << " steps\n";
count = 0;
Assert( zero == 0 );
intt test2 = query_or_modify( value_root, carry_root, 0, 100, 10, tracer );
Assert(test2 == 1000);
std::cout << "test2 is " << test2 << " in " << count << " steps \n";
count = 0;
intt test3 = query_or_modify( value_root, carry_root, 1, 1000, 0, tracer );
Assert(test3 == 990);
std::cout << "test3 is " << test3 << " in " << count << " steps\n";
count = 0;
intt test4 = query_or_modify( value_root, carry_root, 50, 5000, 87, tracer );
Assert(test4 == 10*(100-50) + 87*(5000-50) );
std::cout << "test4 is " << test4 << " in " << count << " steps\n";
count = 0;
}
虽然这不是你想要的答案,但它可能会让某人更容易地编写它。而且,写这个让我觉得很有趣。希望能有所帮助!
这段代码已在Ideone.com上使用C++0x编译器进行了测试和编译。