Harmans C++ Specifics
Harmans C++ Specifics
https://leetcode.com/discuss/study-guide/1154632/C%2B%2B-STL-powerful-guide-or-
Compiled-list-of-popular-STL-operations
https://leetcode.com/discuss/study-guide/1359115/All-C%2B%2B-STL-internal-
implementation-oror-last_minute_notes-oror-2021
###################################################################
1) USING TREESETS AND TREE MAPS C++
Round 1
Design a data structure with two operations 1. addRange(int start, int end)
and 2. contains(int point)
Here range means an interval, so the data structure contains information
of all ranges added uptil that point and you can have interleaved queries
of the form contains(int point) which returns true if the point
is contained in any of the ranges or false otherwise.
// map::lower_bound/upper_bound
#include <iostream>
#include <map>
int main ()
{
std::map<char,int> mymap;
std::map<char,int>::iterator itlow,itup;
mymap['a']=20;
mymap['b']=40;
mymap['c']=60;
mymap['d']=80;
mymap['e']=100;
// print content:
for (std::map<char,int>::iterator it=mymap.begin(); it!=mymap.end(); ++it)
std::cout << it->first << " => " << it->second << '\n';
return 0;
}
#include <iostream>
#include <map>
using namespace std;
itlower = MyMMap.lower_bound("CAN");
itupper = MyMMap.upper_bound("JPN");
MyMMap.erase(itlower, itupper);
Output
MyMMap contains:
USA New York
USA Washington
#include<bits/stdc++.h>
using namespace std;
int main()
{
// initializing vector of integers
vector<int> arr = {10, 15, 20, 25, 30, 35};
Output:
15 exists in vector
23 does not exist
#####
2. lower_bound(start_ptr, end_ptr, num) : Returns pointer to
“position of num” if container contains 1 occurrence of num.
Returns pointer to “first position of num” if container contains
multiple occurrence of num. Returns pointer to “position of next higher
number than num” if container does not contain occurrence of num.
Subtracting the pointer to 1st position i.e “vect.begin()” returns the actual
index.
int main()
{
// initializing vector of integers
// for single occurrence
vector<int> arr1 = {10, 15, 20, 25, 30, 35};
#####
3. upper_bound(start_ptr, end_ptr, num) : Returns pointer to
“position of next higher number than num” if container
contains 1 occurrence of num. Returns pointer to “first position of next
higher number than last occurrence of num” if container contains
multiple occurrence of num. Returns pointer to “position of next
higher number than num” if container does not contain occurrence of num.
Subtracting the pointer to 1st position i.e “vect.begin()” returns the actual
index.
int main()
{
// initializing vector of integers
// for single occurrence
vector<int> arr1 = {10, 15, 20, 25, 30, 35};
3) MAP VS MULTIMAP AND USING COMPARATORS TO BUILD RED BLACK TREE IN MAP:
std::map takes up to four template type arguments,
the third one being a comparator. E.g.:
struct cmpByStringLength {
bool operator()(const std::string& a, const std::string& b) const {
return a.length() < b.length();
}
};
// ...
std::map<std::string, std::string, cmpByStringLength> myMap;
Alternatively you could also pass a comparator to maps constructor.
my_map["1"] = "a";
my_map["three"] = "b";
my_map["two"] = "c";
my_map["fouuur"] = "d";
Note that first and last are iterators and the element
pointed by last is excluded from the search.
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main ()
{
int inputs[] = {7,8,4,1,6,5,9,4};
vector<int> v(inputs, inputs+8);
#include <iostream>
#include <vector>
#include <numeric> // std::iota
#include <algorithm> // std::sort, std::stable_sort
return idx;
}
Now you can use the returned index vector in iterations such as
Guillaume Jacquenot
8,41855 gold badges3737 silver badges4646 bronze badges
answered Sep 13 '12 at 4:10
Łukasz Wiklendt
3,59022 gold badges1515 silver badges1515 bronze badges
4
Love this answer.If your compiler does not support lambdas, you can use a
class: template<typename T> class CompareIndicesByAnotherVectorValues {
std::vector<T>* _values;
public: CompareIndicesByAnotherVectorValues(std::vector<T>* values) :
_values(values)
{}
public: bool operator() (const int& a, const int& b)
const { return (_values)[a] > (_values)[b]; } }; – Yoav Oct 18 '12 at 7:47
Hey guys i want to implement a priority queue which contains entries of type
'node'.
The code is as follows....
struct node
{
int a;
int b;
};
priority_queue<node,vector<node>, cmp> Y;
struct node
{
int a;
int b;
If you still want to use the functor way, then you should define the functor
class:
struct node
{
int a;
int b;
};
struct cmp
{
bool operator ()(const node& A,const node& B) const
{
return (A.a<B.a) || (A.a==B.a && A.b>B.b);
}
};
. . .
priority_queue<node,vector<node>, cmp> Y;
Member functions
(constructor)
Construct priority queue (public member function )
empty
Test whether container is empty (public member function )
size
Return size (public member function )
top
Access top element (public member function )
push
Insert element (public member function )
emplace
Construct and insert element (public member function )
pop
Remove top element (public member function )
swap
Swap contents (public member function )
USAGE:
Define a pair (u,v) which consists of one element from the first
array and one element from the second array.
Example 1:
class Solution {
public:
vector<pair<int, int>> kSmallestPairs(vector<int>& nums1, vector<int>&
nums2, int k) {
vector<pair<int,int>> result;
if (nums1.empty() || nums2.empty() || k <= 0)
return result;
auto comp = [&nums1, &nums2](pair<int, int> a, pair<int, int> b) {
return nums1[a.first] + nums2[a.second] > nums1[b.first] +
nums2[b.second];};
priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(comp)>
min_heap(comp);
min_heap.emplace(0, 0);
while(k-- > 0 && min_heap.size())
{
auto idx_pair = min_heap.top(); min_heap.pop();
result.emplace_back(nums1[idx_pair.first], nums2[idx_pair.second]);
if (idx_pair.first + 1 < nums1.size())
min_heap.emplace(idx_pair.first + 1, idx_pair.second);
if (idx_pair.first == 0 && idx_pair.second + 1 < nums2.size())
min_heap.emplace(idx_pair.first, idx_pair.second + 1);
}
return result;
}
};
struct compare
{
bool operator() (const pair<int, int> & a, const pair<int, int> & b)
{
return a.first + a.second >= b.first + b.second;
}
};
class Solution {
public:
vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2,
int k) {
if (nums1.empty() || nums2.empty() || k == 0)
return {};
int N = nums1.size();
int M = nums2.size();
for (int i = 0; i < N; i++)
{
for (int j = 0; j < M; j++)
{
que.push({nums1[i], nums2[j]});
}
}
vector<vector<int>> ans;
ans.push_back({});
ans.back().push_back(item.first);
ans.back().push_back(item.second);
}
return ans;
}
};
class Solution {
public:
vector<vector<int>> kSmallestPairs(vector<int>& v1, vector<int>&v2, int k)
{
map<int,vector<pair<int,int>>>mp;
int sz1=v1.size(),sz2=v2.size();
for(int i=0;i<sz1;++i){
for(int j=0;j<sz2;++j)
mp[v1[i]+v2[j]].push_back({v1[i],v2[j]});
}
vector<vector<int>>res;
for(auto it=mp.begin();it!=mp.end();++it){
for(pair<int,int>p:it->second){
if(res.size()==k)
break;
res.push_back({p.first,p.second});
}
}
return res;
}
};
8) Set erase, can use both element as well as iterator is this for all containers?
// erasing from set
#include <iostream>
#include <set>
int main ()
{
std::set<int> myset;
std::set<int>::iterator it;
it = myset.begin();
++it; // "it" points now to 20
myset.erase (it);
myset.erase (40);
it = myset.find (60);
myset.erase (it, myset.end());
return 0;
}
#include <numeric>
Loop in C-string:
char s[100];
for (int i = 0; s[i]; ++i) { ... }
This is extremely useful (also avoids the strlen usage, that you
could forget is O(n) and put on for condition).
This also works because of two’s complement. If you AND any number and it’s
negative you obtain the LSB. Very simple and fast.
Many people don’t know this, but you can have statements split
by commas. This I tend to use in every problem I solve, it reduces
the code and avoids the use of semicolons.
The only down side is that you can’t use with break, continue or
return (not statements :/). So when I have to use any of these I have
to add braces and semicolons.
Scanf on array
int a[100];
for (int i = 0; i < n; ++i) scanf("%d", a+i);
This I don’t like very much because it doesn’t work for arrays
with higher dimensions, but since most problems have at most 1D
input it’s quite useful too (although I tend to use the &a[i],
but my teammates use this trick).
First you don’t lose the original order (for offline algorithms this can be
necessary).
Second you can do the same for how many dimensions you have
(for 2D points: x[] and y[]; or 3D: x[], y[] and z[]; or any number
of arrays..) and you don’t have to create structures (lot’s of lines saved)
or use tuple or pairs (these are annoying in competitive programming. Using
pair of pair you have something like: x.first.second + x.second.second and
you line goes extra large and hard to read).
Infinite
const int INF = 0x3f3f3f3f;
This infinite constant is very useful too. I used to do something like x = 2e9;
but I had to take care about not adding infinites (because of integer overflow)
and stuff like this. This constant can be doubled without overflowing and also
can be set very easily this way:
int dist[1000];
memset(dist, 63, sizeof(dist)); // 0x3f == 63
For shortest path algorithms I always use this (I used to tend -1 version,
but I had to do additional checks to verify if dist == -1 and if not..)
and in CP BFS and SSSP are very common problems.
I think that’s all for now. If I remember any other trick I’ll edit this
answer,
but that’s all I can remember for now!
###################################################################################
##############################3
Code forces c++ tips and trick: (https://codeforces.com/blog/entry/74684)
I would like to tell you about some tricks and constructions I use in C++. They
are not hidden or anything, moreover, most of them are from stl or maybe well known
among software engineers, but I often see codes where something is done by hand in,
say, 5 lines, while in stl there is a function which does the same.
all(x)
This may be an exception to the rule of non-popularity -- this is quite widely
used, but some next items will depend on all(x), so I define it here. So, I talk
about
However, it's not all about this define. Imagine you need to build a convex
hull of a set of points. Usually the code looks approximately as follows:
std::unique
This is also well known, but not as well as I want, so I leave it here.
std::unique(begin, end) goes from begin to end, removes consecutive duplicates and
returns the end of the resulting iterator range. So, for example, if vec = {1, 1,
2, 2, 3, 2, 1}, then unique(all(vec)) makes it equal {1, 2, 3, 2, 1, ?, ?} (where ?
is not known, but probably the last two symbols are 2 and 1) and returns
vec.begin() + 5. Usually, to make a vector containing only its unique elements, one
writes
sort(all(vec));
vec.resize(unique(all(vec)) - vec.begin());
or creates a function/define make_unique (which is, actually, also a name of
some existing function in C++) which does so.
nxt()
I noticed that many Kotlin Heroes participants often have some functions in
their templates like readLong(), readToken() and so on. I understand that this may
be for the sake of similarity with C++ paradigm where you can just read stuff or
maybe because val (n, k) = readLine()!!.split(' ').map(String::toInt) is too long
and weird for writing it just to read two numbers. However, what doesn't often come
to mind is that a similar function in C++ can simplify a lot of things (or maybe
it's just a matter of taste). So the function may look as follows:
int nxt() {
int x;
cin >> x;
return x;
}
If a usual reading a graph looks like this:
int n, m;
cin >> n >> m;
for (int i = 0; i < m; ++i) {
int u, v;
cin >> u >> v;
--u, --v;
g[u].push_back(v);
g[v].push_back(u);
}
then with your little mriemd nxt() you can write the following:
Also you may change the type to long long or even make the function template,
but I haven't really ever felt I need the last.
Big thanks to my former teammates savinov and sokian for introducing this to me
in their team template back in 2015 when I joined them. nxt() will return later in
this blog.
fill(all(vec), 1);
And if you need to fill it by consecutive numbers, you can use std::iota. Now
the constructor of a Dsu class with two optimizations may look as follows:
int n;
vector<int> parent, rank;
std::generate
If you have a 0-ary function (that is, with no arguments) and want to fill a
range by its calls, instead of writing a for loop you can call std::generate:
filling a vector vec by random values (assume it's rand() calls) may look like
generate(all(vec), rand);
My favourite: to read n followed by n numbers you can write
int n = nxt();
vector<int> a(n);
generate(all(a), nxt);
or, if you don't need the value of n later, even
vector<int> a(nxt());
generate(all(a), nxt);
Bonus: the last three function have a _n analogs. Instead of the end
iterator/pointer, fill_n, iota_n and generate_n take the size as the second
argument. So if you want to read, say, first n numbers into a vector vec of size
greater than n, you can use
generate_n(vec.begin(), n, nxt);
instead of a longer
std::rotate
Assume you write a profile dp. Or a 3-layer bfs. Or something else, where you
need sometimes to cyclically shift a vector by k. If k=1 then one can write a loop
of swaps:
std::merge
If you want to build a segment tree where each node contains a sorted list of
values from the corresponding range then on its initialization you may need to
merge two vectors several times (or if you implement your own merge sort). Some of
you, probably, write some code like this:
set<int> S(all(a));
This is actually very natural and one can deduce that set should have such a
constructor, but I, to my shame, didn't know it until white2302 told me this
recently.
if (S.find(key) != S.end()) {
// ...
}
However, set and map have a .count() method which returns 1 iff the key is in
the container, otherwise 0:
if (S.count(key)) {
// ...
}
The reason why it is called so and has an int type is that std::multiset and
std::multimap also have this method, and for them the method returns the count of
elements, which may exceed 1, obviously. Thanks to my former students dimas.kovas
and krock21 for enlightening me about this.
if (binary_search(all(vec), key)) {
// ...
}
I think the function doesn't need any explanation (it returns bool, so there is
really not much else one can extract from this). Thanks to some guy who used this
method in some easy problem in an SRM.
std::partition_point
If we talk about binary search: assume you have a vector and a predicate p(x)
so that p(x) = true for all elements of some prefix of vector vec and false on all
others. To find the first place where p(x) doesn't hold one can simply use
!!
Assume you want to use a function which maps 0 to 0 and every non-zero number
to 1 (for example, to count non-zero numbers on all prefix subrectangles). The most
observant of you may have notices that this is simply a cast to bool. The easiest
bool casting operator is !! and it works like this:
void show_binary(int n) {
for (int i = 0; i < 20; ++i) {
cout << !!(n & (1 << i));
}
cout << "\n";
}
Instead, one can simply write
Indeed, in the very first version you could actually explicitly specify the
output type by a simple arrow:
if (is_good(f())) {
use_somehow(f());
}
since it costs two calls of f. You may write something like this:
int x = f();
if (is_good(x)) {
use_somehow(x);
}
but this is not very clean and leaves a variable which is not used then, maybe
under a potentially useful name. To avoid this, one can wrap all this into a block
like this:
{
int x = f();
if (is_good(x)) {
use_somehow(x);
}
}
but a shorter version would do the following:
I'm sure I forgot something. I also feel that this blog is like a quick start
guide into stl. Still, I hope a lot of you will find something useful here. C++, as
every other language (programming or natural), is evolving, after all, and we
should keep our eyes on how we can use it efficiently.
###################################################################################
###########
Disclaimer
This is not a rulebook. You can integrate parts of it into your coding style. Don't
overthink it, especially during a contest!
I'm going to use this guide for myself and to teach my students. I'm planning to
make some good stuff in the future following these principles! Stay tuned!
Compiler
Make use of C++17. Use -Wall -Wextra -Wshadow flags for compilation, and try to
eliminate all of the warning messages, this will prevent you from having some silly
bugs. There are more debugging flags like -fsanitize=undefined which helps you
eliminate bugs such as array out-of-range access and integer overflow during
runtime. For more information, check out "Read More" section.
Naming
C++ libraries use snake_case for functions and classes, in order to differentiate
the user-defined code from the standard library, we will use CamelCase.
struct MyPoint {
int x, y;
bool someProperty;
int someMethod() {
return someProperty ? x : y;
}
};
Note
Using snake_case is fine, but be consistent!
Comments
In competitive programming, you usually don't want to write long comments, but in
case you do want to write some comments, write them using // instead of /* */.
Using // enables you to comment out a chunk of code with /* */ while debugging, and
/* /* ... */ */ is a bug!
Example
/* commenting out a block of code - no problem!
*/
Spacing
Control flow statements are if / else / for / while / ...
Indent using 2 or 4 spaces uniformly. Avoid using tabs since it might make the code
stretched out in different websites (e.g. Codeforces) and also in print (e.g.
ICPC). Set your editor to emit spaces when you hit the tab key.
Braces always open on the same line but close on a new line.
Preferably always use braces for control flow statement blocks even if it's
currently only one line.
else should start on the same line after closing brace of if block.
Keep lines a reasonable length, some say 80 columns!
There should be exactly one blank line between methods. This helps you to organize
your code better.
There should be no blank line after an opening brace or before a closing brace.
Binary operations should be spaced from both sides.
Unary operations should only be spaced from the left side.
Brackets <> [] {} are a part of their identifier like a[5], vector<int> or pair{1,
2}.
Parentheses should only be spaced from outside like (-a[3] + b) * (c + d).
Semicolons and commas should only be spaced from the right side.
Question marks and colons should be spaced from both sides (unlike English!). The
only exceptions are labels like public: or switch cases case 10:.
There should not be any extra spaces at the end of each line.
Add a single newline character at the end of the file.
There should be exactly one space after control flow statements like if (flag).
In contrast to control flow statements, function calls should not be spaced like
func(arg).
Preprocess statements should be written like #define SOME_MACRO and #include
<iostream>.
Templates should be written like template <typename T> and a new line.
Scope resolution operator :: is a part of the identifier and should not be spaced.
Pointer and reference are a part of the type, space accordingly! Like int* ptr and
const string& str. To avoid bugs, declare only one pointer per line.
. and -> operator should not be spaced. When -> is used to show return type (like
in lambda expressions) it should be spaced from both sides.
Lambda expressions should be written like [](int x) -> int { return x + 1; }. The
return type can be omitted most of the times. Feel free to expand the body exactly
like a function if it has more than 1 line of code.
When overloading operators, treat them like functions, no spacing in the name like
bool operator!();
Ellipsis ... should only be spaced from the left side.
Example
#include <bits/stdc++.h>
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
vector<Point<int>> v;
for (int i = 0; i < 5; ++i) {
v.push_back({i, i + DIFF});
}
for (auto p : v) {
if (p.x + DIFF == p.y) {
cout << p << '\n';
} else {
cout << "huh!?\n"; // will never get printed!
}
}
}
Output
(0, 10)
(1, 11)
(2, 12)
(3, 13)
(4, 14)
Competitive Coding Recommendations
Use #include <bits/stdc++.h> instead of many includes.
Use using namespace std; instead of typing std:: every time.
Use using instead of typedef, for example using ll = long long;. Rationale: It's
more consistent with the style of modern C++.
Use struct instead of class. Rationale: It defaults to public, and you don't need
encapsulation in competitive programming!
Don't use too many macros but don't be afraid of using macros! Rationale: It's not
easy to debug and read a code full of ugly macros. but we're hackers after all!
Use const for defining a constant instead of #define. Rationale: consts have a
type, and they are evaluated at compile time.
To avoid bugs, you can use curly braces for each case of switch statement.
Use auto to increase readability and decrease code size.
Use braced initializer lists.
Use emplace and emplace_back for containers when dealing with pairs and tuples.
Rationale: (elem1, elem2, ...) instead of ({elem1, elem2, ...}).
Use lambda functions! They're especially useful when passing functions as arguments
like in sort. Don't repeat yourself, use lambda functions in your code instead of
copy/pasting.
Use nullptr instead of NULL or 0.
Boolean values are true and false!
Use ios::sync_with_stdio(false); and cin.tie(nullptr); for a faster I/O using
cin/cout.
Use builtin functions starting with __builtin.
GCD and LCM are available in C++17 under gcd and lcm.
Use C++11 for-each style for loops for (auto& elem : vec).
Use C++17 binding style like for (auto& [key, val] : dic) and auto [x, y] =
myPoint;
Use C++17 template argument deduction pair p{1, 2.5}; instead of pair<int, double>
p{1, 2.5};.
If you have a lot of nested loops and conditions, refactor! You probably should be
using functions.
Never use goto! But be brave enough to use goto when you want to break from several
nested loops (in case you just can't refactor it)!
Some websites like codeforces use the flag -DONLINE_JUDGE to compile your code,
this means that you can remove your cerrs or your debug functions automatically or
redirect input/output to file instead of stdin/stdout, etc.
#ifdef ONLINE_JUDGE
#define cerr if (false) cerr
#endif
// Alternatively this can be done using a local -DLOCAL flag
// when compiling on your machine, and using #ifndef LOCAL instead.
Prefer using normal operators like !, &&, ||, ^, ... instead of their alternative
representations not, and, or, xor, .... Rationale: We're not coding in python!
Break down your code into different smaller functions. Your code will be cleaner
and easier to debug.
Don't try to be too clever!
Don't reinvent the wheel! — Make use of standard library!
Use common sense and be consistent!
######################################################################
bool isCyclic();
};
// Constructor
Graph::Graph(int V)
{
this->V = V;
adj = new list<int>[V];
}
// If there is
if (color[v] == GRAY)
return true;
return false;
}
return false;
}
if (g.isCyclic())
cout << "Graph contains cycle";
else
cout << "Graph doesn't contain cycle";
return 0;
}
1) C++ BFS
// Driver Function
int main()
{
// Graph with 7 nodes and 6 edges.
int n = 7;
addEdge(g, 0, 1);
addEdge(g, 0, 2);
addEdge(g, 1, 3);
addEdge(g, 1, 4);
addEdge(g, 2, 5);
addEdge(g, 2, 6);
BFSFull(g, n);
return 0;
}
public:
Graph(int V); // Constructor
Graph::Graph(int V)
{
this->V = V;
adj = new list<int>[V];
}
void Graph::addEdge(int v, int w)
{
adj[v].push_back(w); // Add w to v’s list.
}
return 0;
}
3) Find ARTICULATION POINTS aka CUT VERTICES in a graph
The idea is to use DFS (Depth First Search). In DFS, we follow vertices in
tree form called DFS tree. In DFS tree, a vertex u is parent of another
vertex v, if v is discovered by u (obviously v is an adjacent of u in graph).
In DFS tree, a vertex u is articulation point if one of the
following two conditions is true.
Graph::Graph(int V)
{
this->V = V;
adj = new list<int>[V];
}
return 0;
}
Graph::Graph(int V)
{
this->V = V;
adj = new list<int>[V];
}
return 0;
}
Algorithm
Let's draw a vertical line x=−∞ mentally and start moving this line to the
right. In the course of its movement,
this line will meet with segments, and at each time a segment intersect
with our line it
intersects in exactly one point (we will assume that there are no vertical
segments).
We are interested in the relative order of the segments along the vertical.
Namely, we will store a list of
segments crossing the sweep line at a given time,
where the segments will be sorted by their y-coordinate on the sweep line.
In fact, if one segment was first higher than the other, and then became
lower,
then between these two moments there was an intersection of these two
segments.
Two non-intersecting segments also cannot have the same y-coordinates.
From this it follows that at the moment of the segment appearance we can
find the position for this segment in the queue, and we will
not have to rearrange this segment in the queue any more: its order
relative to other segments in the queue will not change.
It is easy to notice that it is enough only to check the added segment with
its upper and lower neighbors, as well as
when removing the segment — its upper and lower neighbors (which after
removal will become neighbors of each other).
It should be noted that at a fixed position of the sweep line, we must
first add all the segments that start at
this x-coordinate, and only then remove all the segments that end here.
Thus, we do not miss the intersection of segments on the vertex: i.e. such
cases when two segments have a common vertex.
Note that vertical segments do not actually affect the correctness of the
algorithm.
These segments are distinguished by the fact that they appear and disappear
at the same time. However, due to the
previous comment, we know that all segments will be added to the queue
first, and only then they will be deleted.
Therefore, if the vertical segment intersects with some other segment
opened at that moment
(including the vertical one), it will be detected.
Implementation:
struct pt {
double x, y;
};
struct seg {
pt p, q;
int id;
struct event {
double x;
int tp, id;
event() {}
event(double x, int tp, int id) : x(x), tp(tp), id(id) {}
set<seg> s;
vector<set<seg>::iterator> where;
s.clear();
where.resize(a.size());
for (size_t i = 0; i < e.size(); ++i) {
int id = e[i].id;
if (e[i].tp == +1) {
set<seg>::iterator nxt = s.lower_bound(a[id]), prv = prev(nxt);
if (nxt != s.end() && intersect(*nxt, a[id]))
return make_pair(nxt->id, id);
if (prv != s.end() && intersect(*prv, a[id]))
return make_pair(prv->id, id);
where[id] = s.insert(nxt, a[id]);
} else {
set<seg>::iterator nxt = next(where[id]), prv =
prev(where[id]);
if (nxt != s.end() && prv != s.end() && intersect(*nxt, *prv))
return make_pair(prv->id, nxt->id);
s.erase(where[id]);
}
}
The main function here is solve(), which returns the number of found
intersecting segments, or (−1,−1), if there are no intersections.
Checking for the intersection of two segments is carried out by the
intersect () function,
using an algorithm based on the oriented area of the triangle.
The queue of segments is the global variable s, a set<event>. Iterators
that specify the
position of each segment in the queue (for convenient removal of segments
from the queue)
are stored in the global array where.
Two auxiliary functions prev() and next() are also introduced, which return
iterators to the previous and next elements (or end(), if one does not
exist).
The constant EPS denotes the error of comparing two real numbers
(it is mainly used when checking two segments for intersection).