Mipt 2014 Burunduk1.En
Mipt 2014 Burunduk1.En
Mipt 2014 Burunduk1.En
1 Sqrt decomposition
Consider searching substrings 1 , 2 , . . . , of equal length in the string . For each we are
interested, if is a substring of . We can solve this problem in linear time, in ( + ||) time, using
hash table of polynomial hashes of the strings . Here you may ask, why don't we use Aho-Corasic?
Let imagine, we just do not know this algorithm, but already heard about hashes and hash tables.
Let we have strings of arbitrary lengths. How to upgrade our solution to previous problem? The
idea of sqrt decomposition
length of all strings as then we may
in
time
(
+
||
)
.
Moreover
we have at most ( ) strings of
iterate lengths less
than
length at least , so summary worktime of solution "group string by length and proceed each
length in linear time" is also ( + || ).
You may use this approach not only string problems. For example, consider problems
about trees.
Let we have
It may contain a lot of vertices of degree less than , but there are
tree of vertices.
at most vertices of degree .
tree as well
as
Fenwick
tree
is
[(log
),
(log
)]
data
structure.
Below
we'll
describe
[(1),
(
)]
Solution #2 in [(1), ()]. Let maintain the same array , and partial sums on it. Moreover let
maintain partial sums for each segment [..( + 1)). To change [] we have to fully recalculate two
1/5
arrays of partial sums (sums of small part, sums of ). To get sum on the segment, we may view it
as head + body + tail. For each of three parts we may get the sum in (1) time.
Solution #3 in [(), ()]. Let maintain partial sums sum[i+1]=sum[i]+a[i] and array of
changes, which have been applied to array since partial sums were calculated. Denote with array
as . One change is pair , . It denotes operation a[i]+=x. Let maintain property
|| . Query "sum on the segment": sum on the segment [, ] in initial array was equal
to sum[r+1]-sum[l], in (||) time we may calculate, how much it was changed. Query
"change []": to set [] := , let add to the pair , [], and put into []. If now
|| > then build partial sums of current version of the array in time (), and clear the
list . Let denote this operation . Note we'll call
not so often. One time per
queries. So amortized time of proceeding one query is ( ) = ( ).
Last approach we'll call delayed operations. This approach has no advantages solving this task.
But it will be useful later.
:
|
|
let split the array into = segments of length . For each of segments we'll call operation
Build which sorts the segments and calculates partial sums. The time spent for each segment is
log Now let describe the main operation Split(i), which returns such , that -the element is
the beginning of -th segment. If is not any beginning, nd = [, ), such that < < , and
split it into two segments = [, ) and = [, ). For segments and call Build, we've got new
partition of the array into segments: = [1 ,
2 , . . . , 1 , , , +1 , . . . , ]. Time to proceed
one Split(i) is () + (build( )), means if = time is log . Here we assume stores not
the segments, but links to it, so to "copy" segment we need only (1) of time. We have Split(i).
Now let express all other operations in terms of Split(i).
vector<Segment*> T;
int Split( int i ) { ... }
2/5
void Insert(i, x) {
a[n++] = x;
int j = Split(i);
T.insert(T.begin() + j, new Segment(n-1, n));
}
void Erase(i) {
int j = Split(i);
split(i + 1);
T.erase(T.begin() + j);
}
int Sum(l, r, x) { // [l, r]
l = split(l), r = split(r + 1); // [l, r)
int res = 0;
while (l <= r)
res += T[l++].get(x); // binary search and use partial sums
return res;
}
To proceed queries of type Reverse, we need to store additional ag "is the segment reversed",
implementation of Split(i) becomes bit more complicated, all other parts stay the same.
void Reverse(l, r) {
l = split(l), r = split(r + 1);
reverse(T + l, T + r)
while (l <= r)
T[l++].reversed ^= 1;
}
= log .
let rebuild all the structure in ( log ) time. Amortized time ofone rebuild is log
In total our solution can proceed any query in amortized time ( log ).
We may speed up it. Note, Split(i) may be done in linear time, more precisely ( + ), rebuild
may be also done in liner time, in (). So let number of segments is equal to / log , amortized
time to proceed one query of type Sum is (++), where time of Split(i), is equal to ( +),
time of inner for-loop of function
Sum, is equal to ( log ), amortized time per one rebuild,
4 Semiplanes intersection
Let solve one complicated problem acm.timus.ru:1390. The statement is: we stay on the plane at
point (0, 0). From time to time walls appear on the plane. The walls are segments do not containing
(0, 0). The walls may intersect each other. From time to time we launch the bullet. The bullet ies
3/5
in a straight line, while does not touch a wall. Answer to launch-bullet-query (type = "bullet") is
distance, which buller has ied (possibly innity).
In total we'll learn to proceed queries of arbitrary type in time ( log2 ). But at rst let try
to understand how to approach this problem. Problem is complicated. Queries of dierent types in
arbitrary order, walls may intersect... Let's try to simplify the task. Let there are some walls, but
no new walls will appear. Moreover let all the walls are straight innite lines.
Now the task is following: we have straight lines, which cut out convex polygon, containing
point (0, 0), we need to proceed queries kind of by given ray from (0, 0) nd intersection point of the
ray and the border of the polygon. Solution to the task: let intersect all semiplanes in ( log ),
we'll get an convex polygon. Then each query can made by binary search in (log ) time. Describe
the solution more precisely. Let start from binary search. Take angle of any polygon's vertex and
denote it by . For each other vertex take angle from [, + 2), write down this numbers in
ascending order, we'll get an array of angles, denote it . To know, where the bullet ying into
(, ) will stop, let calculate its angle [, + 2) and nd (using binary search) such minimal
: [] < [ + 1]. Finally we simply intersect the segment, formed by points , + 1 with the
ray (trace of the bullet). Time is (log ). Now let talk about semiplanes intersection. At rst, let
add semiplanes , , , for some big . Now we are sure, intersection
is nite convex polygon. Sort all lines by its angle (angle is from 0 to 2 ). If several lines have the
same angle, let leave only one among them the nearest to (0, 0). Then we'll add lines in such order
into the stack. Before addition of new line, some top lines in stack may be popped out. Let two top
lines on the stack are 1 and 2 . The top line should be popped out, if point = (1 , 2 ) lies
in opposite side from new line than (0, 0). In the stack we obtain the answer, the polygon. To obtain
whole polygon, let add all the lines twice in the same order. Now contents of the stack is head + the
answer + tail. To cut o the tails consider points of intersection of adjacent lines on the stack and
take any point, which meets twice. Between these two positions the answer lies. The time to build
the structure is () + ().
Complicate the problem: let some new walls (lines) appear. We may use approach of "delayed
items
we
may
add
in
time
(
log
+ ). In total time to proceed
are already sorted,
and
new
queries is ( ).
We may do it in other way. Let store vertices of the polygon in balanced search tree (before
we stored it in sorted array). To add new line with direction vector and normal (direction of
normal is out of (0, 0)), we need nd the vertex of the polygon such that scalar product , is
maximal possible. It can be done by binary search by sorted array or, if we have BST (and we have),
by descent throw BST tree from root to leaves If the point lies at the same side from the new
line, as point (0, 0) does, we should do nothing. Else we have to erase point and, possibly, some
other adjacent points. Finally we have to add two new points instead of all deleted data. Let delete
vertices in loop, until ordinary point is at the same side from the line as point (0, 0) does. Finally
we add intersection of the line and the segment (process of deletion stops, it gives us two points
the last deleted, the rst not deleted). So the time to add new line is (log + log ), where
number of already deleted points. Each point will be deleted only once, so in total we can proceed
queries in ( log ). In terms of speed this solution is much better than previous one. In terms
4/5
5/5