Cod sursa(job #1714517)

Utilizator Andrei1998Andrei Constantinescu Andrei1998 Data 8 iunie 2016 16:08:20
Problema Metrou4 Scor 100
Compilator cpp Status done
Runda Arhiva ICPC Marime 5.6 kb
#include <fstream>
#include <algorithm>
#include <vector>

#define lint long long int
using namespace std;

class InputReader {
    public:
        InputReader() {}
        InputReader(const char *file_name) {
            input_file = fopen(file_name, "r");
            cursor = 0;
            fread(buffer, SIZE, 1, input_file);
        }
        inline InputReader &operator >>(int &n) {
            while(buffer[cursor] < '0' || buffer[cursor] > '9') {
                advance();
            }
            n = 0;
            while('0' <= buffer[cursor] && buffer[cursor] <= '9') {
                n = n * 10 + buffer[cursor] - '0';
                advance();
            }
            return *this;
        }
    private:
        FILE *input_file;
        static const int SIZE = 1 << 17;
        int cursor;
        char buffer[SIZE];
        inline void advance() {
            ++ cursor;
            if(cursor == SIZE) {
                cursor = 0;
                fread(buffer, SIZE, 1, input_file);
            }
        }
};

const int NMAX = 150005;
int n;

struct Point {
    int x, y;
    int ind;

    Point(int _x = 0, int _y = 0, int _ind = 0):
        x(_x), y(_y), ind(_ind) {}
} points[NMAX];

bool operator<(const Point &a, const Point &b) {
    if (a.x != b.x)
        return a.x < b.x;
    return a.y < b.y;
}

void reflect() {
    for (int i = 1; i <= n; ++ i)
        points[i].x = -points[i].x;
}

void rotate() {
    for (int i = 1; i <= n; ++ i) {
        int old_x = points[i].x;
        int old_y = points[i].y;

        points[i].x = -old_y;
        points[i].y =  old_x;
    }
}

struct Node {
    int st, dr;

    int best;
    int ind_best;

    Node(int _st = 0, int _dr = 0, int _best = 0, int _ind_best = 0):
        st(_st), dr(_dr), best(_best), ind_best(_ind_best) {}

    void add_point(const Point &p) {
        if (!ind_best || p.x + p.y > best)
            best = p.x + p.y, ind_best = p.ind;
    }
} tree[4 * NMAX];

void build(int node, int st, int dr) {
    tree[node].st = st, tree[node].dr = dr;
    tree[node].best = tree[node].ind_best = 0;

    if (st == dr)
        return ;

    int mid = (st + dr) >> 1;
    build(2 * node, st, mid);
    build(2 * node + 1, mid + 1, dr);
}

void update(int node, int where, const Point &p) {
    tree[node].add_point(p);

    if (tree[node].st == tree[node].dr)
        return ;

    int mid = (tree[node].st + tree[node].dr) >> 1;
    if (where <= mid)
        update(2 * node, where, p);
    else
        update(2 * node + 1, where, p);
}

pair <int, int> query(int node, int where) {
    if (tree[node].dr == where)
        return make_pair(tree[node].best, tree[node].ind_best);

    int mid = (tree[node].st + tree[node].dr) >> 1;

    pair <int, int> ans = make_pair(0, 0);
    if (where <= mid)
        ans = query(2 * node, where);
    else {
        pair <int, int> aux = query(2 * node, mid);

        if (!ans.second || (aux.second && aux.first > ans.first))
            ans = aux;

        aux = query(2 * node + 1, where);
        if (!ans.second || (aux.second && aux.first > ans.first))
            ans = aux;
    }

    return ans;
}

int father[NMAX];
int h[NMAX];

void init() {
    for (int i = 1; i <= n; ++ i)
        father[i] = i, h[i] = 0;
}

int find(int node) {
    if (father[node] != father[father[node]])
        father[node] = find(father[node]);
    return father[node];
}

bool unite(int a, int b) {
    a = find(a), b = find(b);

    if (a == b)
        return false;

    if (h[a] < h[b])
        father[a] = b;
    else {
        if (h[a] == h[b])
            ++ h[a];
        father[b] = a;
    }

    return true;
}

int m;
struct Edge {
    int a, b, c;

    Edge(int _a = 0, int _b = 0, int _c = 0): a(_a), b(_b), c(_c) {}
} edges[8 * NMAX];

bool operator<(const Edge &a, const Edge &b) {
    return a.c < b.c;
}

vector <int> all_positions;

void sweep() {
    //Normalize
    all_positions.clear();
    for (int i = 1; i <= n; ++ i)
        all_positions.push_back(points[i].y - points[i].x);
    sort(all_positions.begin(), all_positions.end());
    all_positions.resize(unique(all_positions.begin(), all_positions.end()) - all_positions.begin());

    build(1, 1, all_positions.size());

    //Sort
    sort(points + 1, points + n + 1);

    //Sweep
    pair <int, int> aux;

    int coord;
    for (int i = 1; i <= n; ++ i) {
        coord = lower_bound(all_positions.begin(), all_positions.end(), points[i].y - points[i].x) - all_positions.begin() + 1;

        aux = query(1, coord);
        if (aux.second)
            edges[++ m] = Edge(points[i].ind, aux.second, points[i].x + points[i].y - aux.first);
        update(1, coord, points[i]);
    }
}

void get_edges() {
    m = 0;

    for (int k = 0; k < 2; ++ k) {
        for (int i = 0; i < 4; ++ i) {
            sweep();
            rotate();
        }
        reflect();
    }
}

lint kruskal() {
    sort(edges + 1, edges + m + 1);
    init();

    lint ans = 0;
    for (int i = 1; i <= m; ++ i)
        if (unite(edges[i].a, edges[i].b))
            ans += edges[i].c;

    return ans;
}

int main()
{
    InputReader cin("metrou4.in");
    ofstream cout("metrou4.out");

    int t = 1;
    cin >> t;

    while (t --) {
        cin >> n;
        for (int i = 1; i <= n; ++ i) {
            cin >> points[i].x >> points[i].y;
            points[i].ind = i;
        }

        get_edges();
        cout << kruskal() << '\n';
    }

    //cin.close();
    cout.close();
    return 0;
}