Problem:
Score : 1700 points

Problem Statement

We have a grid with N rows and N columns.
The cell at the i-th row and j-th column is denoted (i, j).

Initially, M of the cells are painted black, and all other cells are white.
Specifically, the cells (a_1, b_1), (a_2, b_2), ..., (a_M, b_M) are painted black.

Snuke will try to paint as many white cells black as possible, according to the following rule:

If two cells (x, y) and (y, z) are both black and a cell (z, x) is white for integers 1≤x,y,z≤N, paint the cell (z, x) black.

Find the number of black cells when no more white cells can be painted black.

Constraints

1≤N≤10^5

1≤M≤10^5

1≤a_i,b_i≤N

All pairs (a_i, b_i) are distinct.

Input

The input is given from Standard Input in the following format:

N M
a_1 b_1
a_2 b_2
:
a_M b_M

Output

Print the number of black cells when no more white cells can be painted black.

Sample Input 1

3 2
1 2
2 3

Sample Output 1

3

It is possible to paint one white cell black, as follows:

Since cells (1, 2) and (2, 3) are both black and a cell (3, 1) is white, paint the cell (3, 1) black.

Sample Input 2

2 2
1 1
1 2

Sample Output 2

4

It is possible to paint two white cells black, as follows:

Since cells (1, 1) and (1, 2) are both black and a cell (2, 1) is white, paint the cell (2, 1) black.

Since cells (2, 1) and (1, 2) are both black and a cell (2, 2) is white, paint the cell (2, 2) black.

Sample Input 3

4 3
1 2
1 3
4 4

Sample Output 3

3

No white cells can be painted black.

Solution in Java8 (OpenJDK 1.8.0) (judge verdict: Time Limit Exceeded)

import java.io.IOException;
import java.io.InputStream;
//import java.util.Scanner;
import java.util.Deque;
import java.util.ArrayDeque;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.TreeMap;

/**
 *  http://agc006.contest.atcoder.jp/tasks/agc006_f
 * 
 * @author Cummin
 */
public class Main {
    static int N ;
    static int M ;
    static int pos[][] ;
    static Map<Long, Integer> mat ;      // 二次元を一次元にしたmap
    static Map<Integer, List> Y_index = new TreeMap<>();  // <Key=y, Value=x>とした、逆引き辞書(yは重複がある)
    static int black_cnt ;
    static Deque<Pos> queue = new ArrayDeque<>() ; // チェックが必要なPosのキュー
    static class Pos {
        int x ;
        int y ;
    }

    public static void main(String[] args) {
        int inp[] ;
        inp =fast_read(2);   // sc.nextInt()から変更 2016.11.3.二回目
        N = inp[0] ;
        M = inp[1] ;
        pos = new int[2][M] ;
        mat = new HashMap<>(N*2) ;
        for (int i=0 ; i<M; i++) {
            inp = fast_read(2) ;
            pos[0][i] = inp[0] -1;  // x, y
            pos[1][i] = inp[1] -1;
            Pos p = new Pos() ;  // 毎回 newをする必要がある
            p.x= pos[0][i] ;
            p.y= pos[1][i] ;
            set_bit(p) ;
            add_y_index(p) ;
            queue.offerLast(p);
        }
        //
        black_cnt = M ;
        solve() ;
        System.out.println(black_cnt);
        //dump_mat() ;
    }
    static void add_y_index(Pos p) {
        List list ;
//        System.out.printf("Add_index to Y_index: (x:%d, y:%d)  list= ", p.x+1, p.y+1) ;
        if (Y_index.get(p.y)==null) {
            list = new ArrayList() ;
            list.add(p.x) ;
            Y_index.put(p.y, list) ;            
        } else {
            list = Y_index.get(p.y) ;
            list.add(p.x) ;
        }
    }

    static void solve() {
        Pos p ;
        while(true) {
            p=queue.pollLast() ;
            if (p==null) break ;
            check_and_set(p) ;
        }
    }
    
    static void check_and_set(Pos p_xy) {
        int x = p_xy.x ;
        int y = p_xy.y ;
        Pos p_yz = new Pos() ; // (y,z)
        Pos p_zx = new Pos() ; // (z,x)
        for (int z = 0; z < N; z++) {
            p_yz.x = y ;
            p_yz.y = z ; 
            if (get_bit(p_yz) == 1) {
                p_zx.x = z ;
                p_zx.y = x ;
                if (get_bit(p_zx) == 0) {
                    set_bit(p_zx);
                    black_cnt++;
                    queue.add(p_zx) ; // 新たに埋めた点を追加
                    add_line(p_yz) ;  // p=(y,z) z => Y となる (X*,Y)をすべてqueueに追加する
                    add_y_index(p_zx) ;
                }
            }
        }
    }
    static void add_line(Pos p) {
        List list = Y_index.get(p.y) ;
        if (list == null) return ;
        Pos pp = new Pos() ;
        for (Object i: list) {
            pp.x = (int) i ;
            pp.y = p.y ;
            queue.add(pp) ;
        }
    }
    
    static void set_bit(Pos p) {
        Long index = ((long)p.x<<32) + p.y ;
        if (mat.get(index)==null) {
            mat.put(index,1) ;
        }
    }
    static int get_bit(Pos p) {
        Long index = ((long)p.x<<32) + p.y ;
        if (mat.containsKey(index)) {
            return 1 ;
        } else {
            return 0 ;
        }
    }
    static void dump_mat() {
        Pos pp = new Pos() ;
        for (int iy=0 ;iy<N ;iy++) {
            pp.y = iy ;
            for (int ix=0 ;ix<N; ix++) {
                pp.x = ix ;
                if (get_bit(pp)==0) System.out.printf(".") ;
                else System.out.printf("*") ;
            }
            System.out.println() ;
        }
    }
        //////////////// 高速読み込み //////////////////////
    
    static int[] fast_read(int N) {
        int elements[] = new int[N];
        int e_cnt = 0;
        char c;
        while (e_cnt < N) {
            c = get_char() ;
            if ((c < '0') || ('9' < c)) {
                continue; // Skip 非数値文字
            }
            elements[e_cnt] = 0;
            while (('0' <= c) && (c <= '9')) {  // 数値文字を変換する
                elements[e_cnt] = elements[e_cnt] * 10 + (int) c - '0';
                c = get_char() ;
            }
//            System.out.printf("\nelement[%d] = %d \n", e_cnt + 1, elements[e_cnt]);
            e_cnt++;
        }
        return elements;
    }
    
    ///////////////// get_char() /////////////////////////////////
    //static char c_buf[] = new char[1024*1024];
    static byte c_buf[] = new byte[1024*1024];
    static int ptr = 0 ;
    static int c_len = 0 ;
    //static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static InputStream ins = System.in;

    static char get_char() {
        char ret;

        if (ptr >= c_len) {
            try {
                //c_len = br.read(c_buf);
                c_len = ins.read(c_buf);
            } catch (IOException ex) {
               ex.printStackTrace();
               System.exit(-1); // 終了する
            }
            ptr = 0;
        }
        ret = (char) c_buf[ptr];
        ptr++;
        return ret;
    }
}