// Tetris.java import java.util.*; import java.io.*; // The upper contour is maintained as a linked list of points ordered // left to right. Dropping a rectangle onto the contour involves splicing // the rectangle's top and side edges into the list. // An alternative (simpler) approach maintains a buffer buf[0..1000] // of height values from which the upper contour can be reconstructed. // The height buffer is initialized to all zeros, and then updated // for each rectangle in turn. public class Tetris { public static void main(String[] args) { try { BufferedReader in = new BufferedReader( new InputStreamReader(System.in) ); String s = in.readLine(); int m = Integer.parseInt(s); String res = ""; for (int i = 0; i < m; i++) res += solveProblemInstance(in) + "\n"; System.out.print(res); } catch (IOException e) { System.out.println("i/o exception on input file"); System.exit(1); } } static String solveProblemInstance(BufferedReader in) { Contour contour = new Contour(); Vector rectangles = getInput(in); Iterator iter = rectangles.iterator(); while (iter.hasNext()) { Rectangle r = (Rectangle)iter.next(); contour.addRectangle(r.minx, r.maxx, r.height); } return contour.points(); } static Vector getInput(BufferedReader in) { Vector res = new Vector(); try { String s = in.readLine(); int n = Integer.parseInt(s); for (int i = 0; i < n; i++) { s = in.readLine(); StringTokenizer t = new StringTokenizer(s); int xmin = Integer.parseInt(t.nextToken()); int xmax = Integer.parseInt(t.nextToken()); int height = Integer.parseInt(t.nextToken()); res.add( new Rectangle(xmin, xmax, height) ); } } catch (IOException e) { System.out.println("i/o exception on input file"); System.exit(1); } return res; } } class Contour { List list; public static final int DefaultMinx = 0, DefaultMaxx = 1000; public Contour(int minx, int maxx) { list = new LinkedList(); list.add( new PointI(minx, 0) ); list.add( new PointI(maxx, 0) ); } public Contour() { this(DefaultMinx, DefaultMaxx); } public int maxHeight(int minx, int maxx) { ListIterator iter = list.listIterator( indexOfSuccessor(minx) ); PointI p = (PointI)iter.next(); int cur = p.getY(); int lastIndex = indexOfSuccessor(maxx); while (iter.nextIndex() < lastIndex) { p = (PointI)iter.next(); if (cur < p.getY()) cur = p.getY(); } return cur; } public void addRectangle(int minx, int maxx, int height) { // height of new rectangle after it is added to contour int combinedHeight = height + maxHeight(minx, maxx); // remove vertices of countour covered by the new rectangle int firstIndex = indexOfSuccessor(minx); ListIterator iter = list.listIterator( firstIndex ); int lastIndex = indexOfSuccessor(maxx); int nbrVerticesToRemove = lastIndex - firstIndex; for (int i = 0; i < nbrVerticesToRemove; i++) { iter.next(); iter.remove(); } // get vertices where new rectangle is to be spliced into PointI p = (PointI)iter.previous(); iter.next(); PointI q = (PointI)iter.next(); iter.previous(); // splice in new rectangle if (p.getX() < minx) iter.add( new PointI(minx, p.getY()) ); if (p.getY() != combinedHeight) // avoid redundant points iter.add( new PointI(minx, combinedHeight) ); if (q.getY() != combinedHeight) iter.add( new PointI(maxx, combinedHeight) ); if (q.getX() > maxx) iter.add( new PointI(maxx, q.getY()) ); removeIfRedundant(p); removeIfRedundant(q); } public void removeIfRedundant(PointI p) { int indx = list.indexOf(p); if ((indx == 0) || (indx == list.size()-1)) return; PointI a = (PointI)list.get(indx-1); PointI b = (PointI)list.get(indx+1); if ((a.getY() == p.getY()) && (p.getY() == b.getY())) list.remove(indx); } // note: if two points of current contour lie at x, the index of the second // point is returned public int indexOfSuccessor(int x) { ListIterator iter = list.listIterator( 0 ); while (true) { PointI p = (PointI)iter.next(); if (x < p.getX()) return iter.previousIndex(); else if (x == p.getX()) return (iter.hasNext() ? iter.nextIndex() : iter.previousIndex()); } } public int indexOfPredecessor(int x) { return indexOfSuccessor(x) - 1; } public String points() { ListIterator iter = list.listIterator(0); String res = ""; while (iter.hasNext()) { PointI q = (PointI)iter.next(); res += q + " "; } return res; } public String toString() { ListIterator iter = list.listIterator(0); PointI p = (PointI)iter.next(), q; String res = ""; while (iter.hasNext()) { q = (PointI)iter.next(); res += p.distance(q) + " "; p = q; } return res; } } class PointI { int x, y; public PointI(int x, int y) { this.x = x; this.y = y; } public int getX() { return this.x; } public int getY() { return this.y; } // assumes this and p share the same x or y coordinate public int distance(PointI p) { if (getX() == p.getX()) return Math.abs(getY() - p.getY()); else return Math.abs(getX() - p.getX()); } public String toString() { String res = "(" + x + "," + y + ")"; return res; } public boolean equals(Object obj) { if (obj instanceof PointI) { PointI q = (PointI)obj; return (this.x==q.getX()) && (this.y==q.getY()); } return false; } } class Rectangle { public int minx, maxx, height; public Rectangle(int minx, int maxx, int height) { this.minx = minx; this.maxx = maxx; this.height = height; } }