Cod sursa(job #1708608)

Utilizator tudalexTudorica Constantin Alexandru tudalex Data 27 mai 2016 15:30:58
Problema Metrou4 Scor Ascuns
Compilator java Status done
Runda Marime 8.61 kb
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.logging.Level;
import java.util.logging.Logger;

/*
 * @author Radu Iacob
 */

public class Main {

    static String INPUT_FILE = "metrou4.in";
    static String OUTPUT_FILE = "metrou4.out";
    
    static final Integer NMAX = 200020;
    static final Integer TMAX = 20;
    static final Integer XMAX = 1000000000;
    
    static int T, N;
    static ArrayList<Point> points = new ArrayList<>();
    static ArrayList<Edge> edges   = new ArrayList<>();
    
    static int active_lines[] = new int[NMAX];
    static AIB tree = new AIB(NMAX);
    
    static int dset[] = new int[NMAX];
    static int rank[] = new int[NMAX];
    
    static class Point implements Comparable<Point> {
        
        public int x, y, idx;
        
        Point(int x, int y, int idx) {
            this.x = x;
            this.y = y;
            this.idx = idx;
        }
        
        void reflect_x() {
            x = -x;            
        }
        
        void reflect_y() {
            y = -y;            
        }
        
        void reflect_diagonal() {
            int tmp = x;
            x = y;
            y = tmp;            
        }

        @Override
        public int compareTo(Point other) {
            if (y == other.y) return x - other.x;
            return other.y - y;
        }
                
    }
    
    static public int distance(Point p1, Point p2) {
        return Math.abs(p1.x - p2.x) + Math.abs(p1.y - p2.y);
    }
    
    static class Edge implements Comparable<Edge>{
        
        public int node1, node2, cost;
        
        Edge(int node1, int node2, int cost) {
            this.node1 = node1;
            this.node2 = node2;
            this.cost  = cost;
        }

        @Override
        public int compareTo(Edge other) {
            return cost - other.cost;
        }
        
    }

    static class AIB {
        
        int line[];
        int idx[];
        int size;
        
        AIB(int size) {
            line = new int[size];
            idx  = new int[size];
            this.size = size;
        }
        
        void reset(int size) {            
            this.size = size;
            Arrays.fill(line, 0, size + 1, Integer.MAX_VALUE);
            Arrays.fill(idx, 0, size + 1, -1);            
        }
        
        // position of the first 1 from the binary representation of 'it'
        int zeros(int it) {
            return (it ^ (it - 1)) & it;
        }
        
        // get the nearest neighbour, from the set of candidates [0 .. it]
        int get(int it) {
            
            int result_line = Integer.MAX_VALUE;
            int result_idx  = -1;
            
            for (; it > 0; it -= zeros(it)) {                
                if (line[it] < result_line) {
                    result_line = line[it];
                    result_idx  = idx[it];                    
                }                
            }
                        
            return result_idx;
        }
        
        // update information about the nearest neighbour
        void update(int it, Point p, int p_idx) {
            
            int p_line = p.y - p.x;
            for(; it <= size; it += zeros(it)) {
                if (line[it] > p_line) {
                    line[it] = p_line;
                    idx[it]  = p_idx;                    
                }
            }            
        }
    }
    

    static void add_edges() {
        
        // sort points so that we traverse them in a zig-zag pattern
        // from the upper-left corner
        Collections.sort(points);
        
        // an 'active set' of nearest-neighbour candidates only needs to
        // account for the number of unique individual lines going north-west to south-east
        int lines_count = points.size();        
        for (int i = 0; i < lines_count; ++i) {
            active_lines[i] = points.get(i).x + points.get(i).y;
        }
        
        Arrays.sort(active_lines, 0, lines_count);
        
        // strip duplicate lines
        int idx = 0;
        for (int i = 1; i < lines_count; ++i) {            
            if (active_lines[i] == active_lines[idx]) {
                continue;            
            }
            active_lines[++idx] = active_lines[i];
        }

        lines_count = idx;        
        tree.reset(lines_count + 1);
        
        for (int i = 0; i < points.size(); ++i) {
            
            Point p = points.get(i);
            int key = p.x + p.y;
            
            int line = Arrays.binarySearch(active_lines, 0, lines_count + 1, key);                        
            int nearest_neighbour = tree.get(line + 1);

            if (nearest_neighbour != -1) {                
                Point neighbour = points.get(nearest_neighbour);
                edges.add(new Edge(p.idx, neighbour.idx, distance(p, neighbour)));                
            }
            
            tree.update(line + 1, p, i);                    
        }
        
    }
    
    
    
    
    static void build_graph() {
            
        // consider the octal partition of the 2d space:
        //    1 2
        //  0     3
        //  7     4
        //    6 5
        // relative to each point, we try to compute the closest neighbour
        // in section '0'. Next, we rotate the plan, so that we can apply
        // the same reasoning for each section.
        
        for (int x = 0; x < 2; ++x) {            
            for (int y = 0; y < 2; ++y) {                
                for (int diag = 0; diag < 2; ++diag) {
                    
                    add_edges();
    
                    for (Point p : points) { p.reflect_diagonal(); }
                }
                for (Point p : points) { p.reflect_y(); }
            }
            for (Point p : points) { p.reflect_x(); }
        }        
    }
    
    static int find(int node) {
        if (node != dset[node]) {
            dset[node] = find(dset[node]);
        }
        return dset[node];
    }
    
    static boolean unite(int node1, int node2) {
        
        int root1 = find(node1);
        int root2 = find(node2);
        
        if (root1 == root2) {
            return false;
        }
        
        if (rank[root1] > rank[root2]) {
            dset[root2] = root1;
            rank[root1] += rank[root2];
        } else {
            dset[root1] = root2;
            rank[root2] += rank[root1];
        }

        return true;        
    }
    
    static long compute_mst_cost() {
        
        int N = points.size();
                
        Arrays.fill(rank, 1);        
        for (int i = 0; i < N; ++i) {
            dset[i] = i;
        }
        
        Collections.sort(edges);
        
        long mst_cost = 0;        
        for (Edge edge : edges) {
                        
            int node1 = edge.node1;
            int node2 = edge.node2;
            if (unite(node1, node2)) {
                mst_cost += (long) edge.cost;
            }            
        }
        
        return mst_cost;
    }

    
    static public void solve() {
        
        
        try (BufferedReader br = new BufferedReader((new FileReader(INPUT_FILE)));
             PrintWriter writer = new PrintWriter(OUTPUT_FILE);) {
                       
           T = Integer.parseInt(br.readLine());
           
           while (T-- > 0) {
               
               points.clear();
               edges.clear();

               N = Integer.parseInt(br.readLine());
               
               for (int i = 0; i < N; ++i) {
                   int X, Y;
                   
                   String[] params = br.readLine().split(" ");
                   X = Integer.parseInt(params[0]);
                   Y = Integer.parseInt(params[1]);                   
                   points.add(new Point(X, Y, i));                   
               }
               
               build_graph();
               
               for (int i = 0; i < N; ++i) {
                   Point tmp = points.get(i);
                   points.set(i, points.get(tmp.idx));
                   points.set(tmp.idx, tmp);
               }
               
               long ans = compute_mst_cost();               
               writer.write(String.valueOf(ans) + "\n");            
           }
           
        } catch (IOException ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }
               
    }
    
    public static void main(String... args) {    
        solve();        
    }
    
}