#region ZIPPER using System; using System.Collections.Generic; using System.Collections; using System.Linq; using System.Text; using sc = Scanner; using ci = CharInterpreter; using System.Numerics; //using Geometry; //using gl = Geometry.GeometryLibrary; using grl = GraphLibrary; class Program { static void Main(string[] args) { Solver solver = new Solver(); solver.Solve(); #if DEBUG System.Console.WriteLine("続行するには何かキーを押してください..."); System.Console.ReadKey(); #endif } } /// /// 標準入力読み取り支援,自作(某最速の人を参考) /// #endregion ZIPPER public class Solver { #region IGNORE_ME public Solver() { //こんすとらくたん きにしなくてよろしい } #endregion IGNORE_ME public int MOD = 1000000007;//10^9 + 7 public void Solve() { int n = sc.NextInt(); int[][] xy = new int[n][]; for (int i = 0; i < n; i++) { xy[i] = sc.NextIntArray(); } HashSet[] hash = new HashSet[20001]; for (int i = 0; i < hash.Length; i++) { hash[i] = new HashSet(); } int ans = 0; for (int i = 0; i < n; i++) { int x = xy[i][0]; int y = xy[i][1]; bool canSet = true; for (int j = Math.Max(0, x - 20); j <= x + 20 && j <=20000; j++) { if (hash[j].Count != 0) { for (int k = Math.Min(0,y - 20); k <= y + 20 && k <=20000; k++) { if (hash[j].Contains(k)) { if ((x - j)*(x - j) + (y - k)*(y - k) < 400 ) { canSet = false; j = x + 20 + 10; k = y + 20 + 10; } } } } } if (canSet) { hash[x].Add(y); ans++; } } Console.WriteLine(ans); #if DEBUG Console.WriteLine("t");//local check #endif } } public static class GraphLibrary { public struct Edge { public int From; public int To; public int Cost; public Edge(int from, int to, int cost) { this.From = from; this.To = to; this.Cost = cost; } } public class Graph { public List[] G; /// /// 頂点のインデックスは 0 ~ v-1; /// /// 頂点数 public Graph(int v) { G = new List[v]; for (int i = 0; i < G.Length; i++) { G[i] = new List(); } } public void AddEdge(Edge edge) { G[edge.From].Add(edge); } } #region UTILITY static bool IsInfinity(int a) { return a == int.MaxValue; } #endregion UTILITY /// /// 最短経路長を求める /// /// 点の数 /// 始点 0 ~ v-1 /// 辺の情報 /// d[i]: sからiへの最短経路長 public static int[] BellmanFord(int v, int s, Edge[] ed) { int[] d = new int[v]; for (int i = 0; i < v; i++) d[i] = int.MaxValue; d[s] = 0; while (true) { bool update = false; for (int i = 0; i < ed.Length; i++) { Edge e = ed[i]; if (!IsInfinity(d[e.From]) && !IsInfinity(e.Cost) && d[e.To] > d[e.From] + e.Cost) { d[e.To] = d[e.From] + e.Cost; update = true; } } if (!update) break; } return d; } /// /// 負の閉路が存在するかどうか /// /// 頂点の数 /// 辺の集合 /// 負の閉路が存在するかどうか public static bool DoesHaveNegativeLoop(int v, Edge[] ed) { int[] d = new int[v]; for (int i = 0; i < v; i++) { for (int j = 0; j < ed.Length; j++) { Edge e = ed[j]; if (d[e.To] > d[e.From] + e.Cost) { d[e.To] = d[e.From] + e.Cost; if (i == v - 1) return true;//この時の更新があったら閉路があるらしい } } } return false; } /// /// 負の閉路がある場合、無視できるかどうか /// /// /// /// /// public static bool CanIgnoreNegativeLoop(int v, int s, Edge[] ed) { Edge[] connectedNodesChecker = new Edge[ed.Length]; for (int i = 0; i < ed.Length; i++) { connectedNodesChecker[i] = new Edge(ed[i].From, ed[i].To, 1); } int[] d = BellmanFord(v, s, connectedNodesChecker); HashSet canReachNodesFromS = new HashSet(); for (int i = 0; i < v; i++) if (!IsInfinity(d[i]))canReachNodesFromS.Add(i); for (int i = 0; i < ed.Length; i++) if (canReachNodesFromS.Contains(ed[i].From)) connectedNodesChecker[i].Cost = ed[i].Cost; return DoesHaveNegativeLoop(v,connectedNodesChecker); } /// /// 最短経路長を求める /// /// 二次元マップ /// d[i][j]]: iからjへの最短経路長 public static int[][] WarshallFloyd(int[][] graph) { int[][] d = new int[graph.Length][]; for (int i = 0; i < d.Length; i++) d[i] = new int[graph[0].Length]; for (int k = 0; k < graph.Length; k++) { for (int i = 0; i < graph.Length; i++) { for (int j = 0; j < graph.Length; j++) { if (!IsInfinity(d[i][k]) && !IsInfinity(d[k][j])) { d[i][j] = Math.Min(d[i][j], d[i][k] + d[k][j]); } } } } return d; } /// /// 連結なグラフの最小全域木を求める /// /// 頂点の数 /// 辺の集合 to, from は気にしなくてよい /// 最小全域木の重み public static int Kruskal(int v, Edge[] ed) { Array.Sort(ed, (a, b) => { return a.Cost - b.Cost; }); UnionFindTree uf = new UnionFindTree(v); int res = 0; for (int i = 0; i < ed.Length; i++) { Edge e = ed[i]; if (!uf.Same(e.To, e.From)) { uf.Unite(e.To, e.From); res += e.Cost; } } return res; } #region SUPPORT_INNER_CLASS /// /// ダイクストラ法に使うつもりの優先度付きキュー /// インナークラス /// class PriorityQueueForGL { int[] array; int count = 0; Comparison comparison; // intに対する比較方向を示すデリゲート, (a,b)のとき返り値intに a_int - b_int (a > b) なら昇順,逆なら降順 public void SetOptionData(Comparison comparison, int MaxCount) { this.comparison = comparison; array = new int[MaxCount]; count = 0; } /// /// ヒープ化されている配列リストに新しい要素を追加する。 /// /// 対象の配列リスト private void PushHeap(int elem) { int n = count; count++; array[n] = elem; while (n != 0) { int i = (n - 1) / 2; // 親と値を入れ替え if (comparison(array[n], array[i]) < 0) { int tmp = array[n]; array[n] = array[i]; array[i] = tmp; } n = i; } } /// /// ヒープから指定した値を削除する。 /// /// 対象の配列リスト private int PopHeap() { if (count == 0) throw new IndexOutOfRangeException("要素が0のキューに要素を取り出す命令がおこなわれました"); int n = count - 1; int returnItem = (int)array[0]; array[0] = array[n]; array[n] = 0; count--; for (int i = 0, j; (j = 2 * i + 1) < n; ) { // 値の大きい方の子を選ぶ if ((j != n - 1) && (comparison((int)array[j], (int)array[j + 1]) > 0)) j++; // 子と値を入れ替え if (comparison((int)array[i], (int)array[j]) > 0) { int tmp = (int)array[j]; array[j] = array[i]; array[i] = tmp; } i = j; } return returnItem; } /// /// 要素のプッシュ. /// /// 挿入したい要素 public void Push(int elem) { PushHeap(elem); } /// /// 要素をポップ.出したデータはコレクションの中から消える /// /// public int Pop() { return PopHeap(); } /// /// 要素を全て消去する /// public void Clear() { for (int i = 0; i < array.Length; i++) array[i] = 0; } } /// /// UF木、蟻本より /// class UnionFindTree { int[] Par; int[] Rank; /// /// nの要素数を管理するUF木 /// /// public UnionFindTree(int n) { this.Par = new int[n]; this.Rank = new int[n]; for (int i = 0; i < n; i++) { Par[i] = i; } } /// /// 木の根を求める /// /// /// private int Find(int x) { if (Par[x] == x) return x; else { Par[x] = Find(Par[x]); return Par[x]; } } /// /// xとyの属する集合を併合 /// /// /// public void Unite(int x, int y) { x = Find(x); y = Find(y); if (x == y) return; if (Rank[x] < Rank[y]) { Par[x] = y; } else { Par[y] = x; if (Rank[x] == Rank[y]) Rank[x]++; } } /// /// xとyが同じ集合に属するか否か /// /// /// /// public bool Same(int x, int y) { return Find(x) == Find(y); } } #endregion SUPPORT_INNER_CLASS } #region STANDARD_READER public static class Scanner { public static string NextString() { string tmp = ""; while (true) { int readData; string data; readData = Console.Read(); if (readData == -1) //EOF { break; } data = char.ConvertFromUtf32(readData); if (data == " " || data == "\n") { break; } tmp += data; } return tmp; } public static int NextInt() { string tmp = ""; while (true) { int readData; string data; readData = Console.Read(); if (readData == -1) //EOF { break; } data = char.ConvertFromUtf32(readData); if (data == " " || data == "\n") { break; } tmp += data; } return int.Parse(tmp); } public static long NextLong() { string tmp = ""; while (true) { int readData; string data; readData = Console.Read(); if (readData == -1) //EOF { break; } data = char.ConvertFromUtf32(readData); if (data == " " || data == "\n") { break; } tmp += data; } return long.Parse(tmp); } public static double NextDouble() { string tmp = ""; while (true) { int readData; string data; readData = Console.Read(); if (readData == -1) //EOF { break; } data = char.ConvertFromUtf32(readData); if (data == " " || data == "\n") { break; } tmp += data; } return double.Parse(tmp); } public static string[] NextStrArray() { return Console.ReadLine().Split(' '); } public static int[] NextIntArray() { string[] s = NextStrArray(); int[] a = new int[s.Length]; for (int i = 0; i < a.Length; i++) { a[i] = int.Parse(s[i]); } return a; } public static long[] NextLongArray() { string[] s = NextStrArray(); long[] a = new long[s.Length]; for (int i = 0; i < a.Length; i++) { a[i] = long.Parse(s[i]); } return a; } public static double[] NextDoubleArray() { string[] s = NextStrArray(); double[] a = new double[s.Length]; for (int i = 0; i < a.Length; i++) { a[i] = double.Parse(s[i]); } return a; } } /// /// 二次元グリッドなどの文字列で与えられたマップなどで、 /// 手軽にデータ変換を適用するためのクラス /// public static class CharInterpreter { /// /// 変換用辞書 /// private static Dictionary MapToInt = new Dictionary(); /// /// 変換法則を追加する /// /// char文字 /// 対応する整数値 public static void AddCorrespondence(char c,int i) { MapToInt.Add(c,i); } /// /// 文字列に対して、対応付けされた /// 例外処理をしていないので注意 /// /// 対応対応がなかった場合はバグる;; public static int Inquiry(char c) { int ret = 0; MapToInt.TryGetValue(c, out ret); return ret; } /// /// 指定された変換法則の元でint[,]の二次元平面を生成する /// 対応関係がない場合の例外処理をしていないので注意 /// /// 配列の各文字列の長さが全て同じでないとうまく作動しないので注意 /// int[ field.length , field[0].length]型の配列 public static int[,] GenerateSquareField(string[] field) { int[,] ret = new int[field.Length, field[0].Length]; for (int i = 0; i < field.Length; i++) { for (int j = 0; j < field[0].Length; j++) { MapToInt.TryGetValue(field[i][j], out ret[i, j]); } } return ret; } } #endregion STANDARD_READER