/**
* Rectangle union
* Input: N (number of rectangles), and then N rectangles (x1 y1 x2 y2)
* Output: area of union of all rectangles
*
* Author: Erel Segal
* Created: 2010-10-07
*
* TODO: Use an Interval Tree: http://e...content-available-to-author-only...a.org/wiki/Interval_tree
* to make RectangleCollection::contains() more efficient.
*/
#include <iostream>
#include <algorithm>
#include <set>
#include <vector>
#include <time.h>
#include <assert.h>
using namespace std;
typedef unsigned long long ulonglong;
template <class Iterator> void print (ostream& out, Iterator iFrom, Iterator iTo) {
for (; iFrom<iTo; ++iFrom) {
out << *iFrom << " ";
}
out << endl;
}
template <class Iterator> void shuffle (Iterator iFrom, Iterator iTo) {
int size = iTo-iFrom;
for (int i=1; i<size; ++i) {
Iterator iCurrent = iFrom+i;
Iterator iNew = iFrom+(rand()%(i+1));
swap(*iCurrent, *iNew);
}
}
template <class T> void unique_vector (vector<T>& v) {
typename vector<T>::iterator new_end = ::unique (v.begin(), v.end());
v.resize(new_end-v.begin());
}
struct Rectangle {
int x1, y1, x2, y2;
Rectangle() {}
Rectangle(int _x1, int _y1, int _x2, int _y2) { x1=_x1; x2=_x2; y1=_y1; y2=_y2; }
friend istream& operator>> (istream& in, Rectangle& r) { in >> r.x1 >> r.y1 >> r.x2 >> r.y2; return in; }
friend ostream& operator<< (ostream& out, const Rectangle& r) { out << r.x1 << " " << r.y1 << " " << r.x2 << " " << r.y2; return out; }
friend ostream& operator<< (ostream& out, const Rectangle* r) { return (out << *r); }
int midx() const { return (x1+x2)/2; }
int midy() const { return (y1+y2)/2; }
bool contains(int x, int y) const { return x1<=x && x<x2 && y1<=y && y<y2; }
bool containsX (int x) const { return x1<=x && x<x2; }
bool containsY (int y) const { return y1<=y && y<y2; }
bool entirelyLefter(int x) const { return x2<=x; }
bool entirelyRighter(int x) const { return x<x1; }
};
bool lefter(const Rectangle* A, const Rectangle* B) {
return A->x1 < B->x1;
}
bool righter(const Rectangle* A, const Rectangle* B) {
return A->x2 > B->x2;
}
typedef vector<Rectangle*> PRectangleVector;
/**
An interval tree for sorting Rectangles by their x value
@see: http://e...content-available-to-author-only...a.org/wiki/Interval_tree
*/
class RectangleTree {
int myThreshold;
RectangleTree* lefterRectangles;
RectangleTree* righterRectangles;
PRectangleVector containingRectanglesSortedByLeft;
PRectangleVector containingRectanglesSortedByRight;
// INVARIANT 1: a tree-node has children if and only if it has rectangles.
// (when we create a tree, we first insert a rectangle, only then we create children).
public:
RectangleTree(): lefterRectangles(NULL), righterRectangles(NULL), containingRectanglesSortedByLeft(), containingRectanglesSortedByRight() {}
bool isEmpty() const { return containingRectanglesSortedByLeft.empty(); }
void insert (Rectangle* newRectangle) {
//cout << "insert " << newRectangle << "(size=" << containingRectanglesSortedByLeft.size() << ")" << endl;
if (isEmpty()) {
//cout << "insert to empty" << endl;
myThreshold = newRectangle->midx();
containingRectanglesSortedByLeft.push_back(newRectangle);
containingRectanglesSortedByRight.push_back(newRectangle);
} else if (newRectangle->containsX(myThreshold)) {
//cout << "insert to containing" << endl;
containingRectanglesSortedByLeft.push_back(newRectangle);
containingRectanglesSortedByRight.push_back(newRectangle);
} else if (newRectangle->entirelyLefter(myThreshold)) {
//cout << "insert to left" << endl;
if (!lefterRectangles)
lefterRectangles = new RectangleTree();
lefterRectangles->insert(newRectangle);
} else if (newRectangle->entirelyRighter(myThreshold)) {
//cout << "insert to right" << endl;
if (!righterRectangles)
righterRectangles = new RectangleTree();
righterRectangles->insert(newRectangle);
} else {
throw string("Impossible case");
}
}
void sort() {
std::sort(containingRectanglesSortedByLeft.begin(), containingRectanglesSortedByLeft.end(), &lefter);
std::sort(containingRectanglesSortedByRight.begin(), containingRectanglesSortedByRight.end(), &righter);
if (lefterRectangles) lefterRectangles->sort();
if (righterRectangles) righterRectangles->sort();
}
bool contains(int x, int y) const {
bool result = false;
if (isEmpty()) {
result = false; // no rectangles -> no children (by invariant 1)
} else if (x<myThreshold) {
// search for containing rectangles that start lefter than x
for (PRectangleVector::const_iterator it=containingRectanglesSortedByLeft.begin(); it!=containingRectanglesSortedByLeft.end(); ++it) {
const Rectangle& rect = **it;
if (rect.contains(x,y))
result = true;
if (rect.entirelyRighter(x))
break; // since the rectangles are sorted
}
if (!result)
// search for lefter rectangles that start lefter than x
result = (lefterRectangles? lefterRectangles->contains(x,y): false);
} else {// (x>=myThreshold)
// search for containing rectangles that end righter than x
for (PRectangleVector::const_iterator it=containingRectanglesSortedByRight.begin(); it!=containingRectanglesSortedByRight.end(); ++it) {
const Rectangle& rect = **it;
if (rect.contains(x,y))
result = true;
if (rect.entirelyLefter(x))
break; // since the rectangles are sorted
}
// search for righter rectangles that start righter than x
if (!result)
result = (righterRectangles? righterRectangles->contains(x,y): false);
}
//cout << "contains("<< x << "," << y << ")=" << result << endl;
return result;
}
void print(ostream& out, string prefix="") const {
out << prefix << "LEFTER THAN " << myThreshold << ":" << endl;
if (lefterRectangles) lefterRectangles->print(out, prefix+" ");
out << prefix << "CONTAINING " << myThreshold << " SORTED BY LEFT: " << endl;
out << prefix; ::print(out, containingRectanglesSortedByLeft.begin(), containingRectanglesSortedByLeft.end());
out << prefix << "CONTAINING " << myThreshold << " SORTED BY RIGHT: " << endl;
out << prefix; ::print(out, containingRectanglesSortedByRight.begin(), containingRectanglesSortedByRight.end());
out << prefix << "RIGHTER THAN " << myThreshold << ":" << endl;
if (righterRectangles) righterRectangles->print(out, prefix+" ");
}
};
class RectangleCollection {
vector<Rectangle> myRectangles;
RectangleTree myRectangleTree;
vector<int> xs; // a sorted array of all x coordinates.
vector<int> ys; // a sorted array of all y coordinates.
int myCount;
bool isSorted;
public:
RectangleCollection(int newCount):
myRectangles(newCount),
myRectangleTree(),
xs(2*newCount), ys(2*newCount), myCount(0), isSorted(false) {}
void add(const Rectangle& rect) {
//cout << "add " << rect << endl;
assert(!isSorted);
myRectangles[myCount] = rect;
xs[2*myCount] = rect.x1;
xs[2*myCount+1] = rect.x2;
ys[2*myCount] = rect.y1;
ys[2*myCount+1] = rect.y2;
++myCount;
}
void sort() {
//cout << "xs: "; ::print (cout, xs.begin(), xs.end());
//cout << "ys: "; ::print (cout, ys.begin(), ys.end());
::sort (xs.begin(), xs.end());
::sort (ys.begin(), ys.end());
//cout << "xs: "; ::print (cout, xs.begin(), xs.end());
//cout << "ys: "; ::print (cout, ys.begin(), ys.end());
::unique_vector (xs);
::unique_vector (ys);
//cout << "xs: "; ::print (cout, xs.begin(), xs.end());
//cout << "ys: "; ::print (cout, ys.begin(), ys.end());
//exit(1);
shuffle(myRectangles.begin(), myRectangles.end());
for (size_t i=0; i<myRectangles.size(); ++i) {
myRectangleTree.insert(&myRectangles[i]);
}
myRectangleTree.sort();
//myRectangleTree.print(cout); cout << "sort end!" << endl;
isSorted=true;
}
bool contains (int x, int y) const {
return myRectangleTree.contains(x,y);
for (vector<Rectangle>::const_iterator i=myRectangles.begin(); i<myRectangles.end(); ++i) {
if (i->contains(x,y))
return true;
}
return false;
}
// calculate the total area
ulonglong area() const {
assert(isSorted);
ulonglong a = 0;
for (int x=0; x<xs.size()-1; ++x) {
for (int y=0; y<ys.size()-1; ++y) {
if (this->contains(xs[x],ys[y])) {
int pixelArea = (xs[x+1]-xs[x]) * (ys[y+1]-ys[y]);
a += pixelArea;
}
}
}
return a;
}
void assertArea(ulonglong expected) const {
time_t before = time(NULL);
size_t a = area();
if (a!=expected) {
cerr << "ERROR: Area should be " << expected << " but was " << a << endl;
} else {
time_t after = time(NULL);
cout << "calculated area of " << myCount << " rects in " << (after-before) << " seconds" << endl;
}
}
}; // RectangleCollection
void testDiagonal0(int N) {
RectangleCollection rects(N);
for (int i=0; i<N; ++i) {
rects.add(Rectangle(2*i,2*i,2*i+1,2*i+1));
}
rects.sort();
rects.assertArea(N);
}
void testDiagonal1(int N) {
RectangleCollection rects(N);
for (int i=0; i<N; ++i) {
rects.add(Rectangle(i,i,i+1,i+1));
}
rects.sort();
rects.assertArea(N);
}
void testDiagonal2(int N) {
RectangleCollection rects(N);
for (int i=0; i<N; ++i) {
rects.add(Rectangle(i,i,i+2,i+2));
}
rects.sort();
rects.assertArea(4*N-(N-1));
}
void testEqual(int N) {
RectangleCollection rects(N);
for (int i=0; i<N; ++i) {
rects.add(Rectangle(0,0,1,1));
}
rects.sort();
rects.assertArea(1);
}
int main() {
int N; // number of rectangles in input
cin >> N;
/*
time_t before = time(NULL);
testDiagonal0(N);
testDiagonal1(N);
testDiagonal2(N);
testEqual(N);
time_t after = time(NULL);
cout << "calculated areas of " << N << " rects in " << (after-before) << " seconds" << endl;
exit(1);
*/
RectangleCollection rects(N);
Rectangle rect;
for (int i=0; i<N; ++i) {
cin >> rect; // input
rects.add(rect);
}
rects.sort();
cout << rects.area() << endl;
}