// Arup Guha
// 10/16/2014 (started a really long time ago)
// Solution to 2013 U-Chicago Problem B: Cans of Worms

import java.util.*;

public class b {

	public static int n;

	public static void main(String[] args) {

		Scanner stdin = new Scanner(System.in);
		n = stdin.nextInt();

		// Go through each case.
		while (n != 0) {

			// Read in cans.
			can[] cans = new can[n];
			for (int i=0; i<n; i++) {
				int x = stdin.nextInt();
				int r = stdin.nextInt();
				cans[i] = new can(x,r,i);
			}

			// Put in order.
			Arrays.sort(cans);

			// To store prevs to see if we need to iterate again.


			// Get the can numbers each can can hit directly on both ends.
			int[] lowTotal = getLowDirect(cans);
			int[] highTotal = getHighDirect(cans);

			while (iter(lowTotal, highTotal));

			// Store these values back in their proper indexes. (This is annoying.)
			int[] lowAns = getAns(lowTotal, cans);
			int[] highAns = getAns(highTotal, cans);

			// Print out number of cans in range.
			for (int i=0; i<n-1; i++)
				System.out.print((highAns[i]-lowAns[i]+1)+" ");
			System.out.println(highAns[n-1]-lowAns[n-1]+1);

			// Get next case.
			n = stdin.nextInt();
		}
	}

	// Reorders answers - really annoying step.
	public static int[] getAns(int[] total, can[] cans) {

		int[] ans = new int[n];
		for (int i=0; i<n; i++) {
			ans[cans[i].index] = total[i];
		}
		return ans;
	}

	// Uses binary search on each can to figure out the lowest can it can hit, and returns this list.
	public static int[] getLowDirect(can[] cans) {
		int[] ans = new int[n];
		for (int i=0; i<n; i++)
			ans[i] = binSearch(cans, cans[i].x-cans[i].r, true);
		return ans;
	}

	// Uses binary search on each can to figure out the highest can it can hit, and returns this list.
	public static int[] getHighDirect(can[] cans) {
		int[] ans = new int[n];
		for (int i=0; i<n; i++)
			ans[i] = binSearch(cans, cans[i].x+cans[i].r, false);
		return ans;
	}

	public static boolean iter(int[] lowDirect, int[] highDirect) {

		int[] anslow = new int[n];
		int[] anshigh = new int[n];

		// Set up interval trees for minimum and maximum.
		IntervalTree low = new IntervalTree(n,true);
		IntervalTree high = new IntervalTree(n,false);
		for (int i=0; i<n; i++) {
			low.add(i, lowDirect[i]);
			high.add(i, highDirect[i]);
		}

		// "Relax" low and high boundaries based on current, seeing if there's an update.
		boolean flag = false;
		for (int i=0; i<n; i++) {
			anslow[i] = low.query(lowDirect[i], highDirect[i]);
			if (anslow[i] != lowDirect[i]) flag = true;
			anshigh[i] = high.query(lowDirect[i], highDirect[i]);
			if (anshigh[i] != highDirect[i]) flag = true;
		}

		// Copy back answers.
		for (int i=0; i<n; i++) {
			lowDirect[i] = anslow[i];
			highDirect[i] = anshigh[i];
		}

		// Return if there were any changes.
		return flag;
	}

	public static int binSearch(can[] cans, int x, boolean gTEQ) {

		// Set up regular binary search.
		int low = 0, high = n-1;
		while (low < high-1) {

			int mid = (low+high)/2;

			// For low search.
			if (gTEQ) {
				if (cans[mid].x >= x)
					high = mid;
				else
					low = mid+1;
			}

			// For high search.
			else {
				if (cans[mid].x <= x)
					low = mid;
				else
					high = mid-1;
			}
		}

		// Just in case low < high.
		if (gTEQ) return cans[low].x >= x ? low : high;
		return cans[high].x <= x ? high : low;
	}
}

class can implements Comparable<can> {

	public int x;
	public int r;
	public int index;

	public can(int myx, int myr, int myi) {
		x = myx;
		r = myr;
		index = myi;
	}

	public int compareTo(can other) {
		return this.x - other.x;
	}

	public boolean hits(can other) {
		return this.r >= Math.abs(this.x - other.x);
	}

	public String toString() {
		return "("+x+","+r+")";
	}
}

class IntervalTree {

	private int[][] tree;
	private int n;
	private int rows;
	private int[] POW2;
	private boolean min;

	// Allows us to fill intervals in [0, size);
	public IntervalTree(int size, boolean isMin) {

		n = 1;
		rows = 1;
		POW2 = new int[32];
		POW2[0] = 1;
		while (n < size) {
			n = n << 1;
			rows++;
			POW2[rows-1] = n;
		}

		// Allocate space for tree.
		min = isMin;
		tree = new int[rows][];
		for (int i=0; i<rows; i++) {
			tree[i] = new int[POW2[i]];
			if (min) Arrays.fill(tree[i], Integer.MAX_VALUE);
			else	 Arrays.fill(tree[i], Integer.MIN_VALUE);
		}
	}

	// Add value in spot index - works bottom up.
	public void add(int index, int value) {
		for (int i=rows-1,level=1; i>=0; i--,level*=2) {
			if (min)
				tree[i][index/level] = Math.min(tree[i][index/level], value);
			else
				tree[i][index/level] = Math.max(tree[i][index/level], value);
		}
	}

	// high is inclusive
	public int query(int low, int high) {
		return query(low, high, 0);
	}

	public int query(int low, int high, int level) {

		int factor = n/POW2[level];
		int lowI = low/factor;
		int highI = high/factor;

		if (lowI == highI && low%factor == 0 && high%factor == factor-1) return tree[level][lowI];

		// Last level - solve.
		if (level == rows-1) {
			if (min) return Math.min(tree[level][lowI],tree[level][highI]);
			else     return Math.max(tree[level][lowI],tree[level][highI]);
		}

		// Last case with lowI == highI, go down tree.
		if (lowI == highI) return query(low, high, level+1);

		// Initialize answer to range within low side of the tree.
		int ans = low%factor != 0 ? query(low, (lowI+1)*factor-1, level+1) : tree[level][lowI];

		// Either recurse or use this branch of the tree.
		if (high%factor != factor-1) {
			if (min) return Math.min(ans, query(highI*factor, high, level+1));
			else     return Math.max(ans, query(highI*factor, high, level+1));
		}
		else {
			if (min) return Math.min(ans, tree[level][highI]);
			else     return Math.max(ans, tree[level][highI]);
		}
	}
}
