Problem:
MathJax.Hub.Config({ tex2jax: { inlineMath: [["$","$"], ["\\(","\\)"]], skipTags: ["script","noscript","style","textarea","code"], processEscapes: true }});

Permutation Period

You have a permutation $p$ of $N$ integers. Initially $p_i = i$ holds for $1 \leq i \leq N$. For each $j$ ($1 \leq j \leq N$), let's denote $p_{j}^0 = j$ and $p_{j}^k = p_{p_j}^{k-1}$ for any $k\geq 1$. The period of $p$ is defined as the minimum positive integer $k$ which satisfies $p_{j}^k = j$ for every $j$ ($1 \leq j \leq N$).

You are given $Q$ queries. The $i$-th query is characterized by two distinct indices $x_i$ and $y_i$. For each query, swap $p_{x_i}$ and $p_{y_i}$ and then calculate the period of updated $p$ modulo $10^9 + 7$ in the given order.

It can be proved that the period of $p$ always exists.

Input

The input consists of a single test case of the following format.

$N$ $Q$
$x_1$ $y_1$
...
$x_Q$ $y_Q$

The first line consists of two integers $N$ and $Q$ ($2 \leq N \leq 10^5, 1 \leq Q \leq 10^5$). The ($i+1$)-th line consists of two integers $x_i$ and $y_i$ ($1 \leq x_i, y_i \leq N, x_i \ne y_i$).

Output

Print the answer in one line for each query.

Sample Input 1

5 4
2 5
2 4
1 3
1 2

Output for Sample Input 1

2
3
6
5

$p$ changes as follows: $[1,2,3,4,5] \rightarrow [1,5,3,4,2] \rightarrow [1,4,3,5,2] \rightarrow [3,4,1,5,2] \rightarrow [4,3,1,5,2]$.

Sample Input 2

2 2
1 2
1 2

Output for Sample Input 2

2
1

$p$ changes as follows: $[1,2] \rightarrow [2,1] \rightarrow [1,2]$.

Sample Input 3

10 10
5 6
5 9
8 2
1 6
8 1
7 1
2 6
8 1
7 4
8 10

Output for Sample Input 3

2
3
6
4
6
7
12
7
8
9

Solution in JAVA (judge verdict: Time Limit Exceeded)


import java.io.*;
import java.math.BigInteger;
import java.util.*;

/**
 * AIZU ONLINE JUDGE
 * 2855 Permutation Period
 *    2018/02/20
 */
public class Main {

    //int[] lcnt;

    // 11.1
    // 18 12.477
    private final static int JOIN_NUM = 80;
    private final static int SPLIT_NUM = JOIN_NUM * 8;

    class Item {
        int num;      // 1-
        int groupNum; // このグループのItem数
        Item next;
        Item nextG;
        Item prevG;

        @Override
        public String toString() {
            String s = "";
            Item p = this;
            for(;;) {
                if (p.nextG != null) {
                    s += String.format("(%d)", p.groupNum);
                }
                //s += String.format("%d ", p.num);
                p = p.next;
                if (p == this)
                    break;
            }
            return s;
        }

        // 所属グループを返す
        Item getGroup() {
            if (nextG != null) {
                return this;
            }
            Item p = this;
            for(;;p = p.next) {
                if (p.nextG != null)
                    return p.prevG;
            }
        }

        // ループ数を求める
        int getLoopNum() {
            Item g = getGroup();
            int num = g.groupNum;
            Item p = g.nextG;;
            for(;p != g;) {
                num += p.groupNum;
                p = p.nextG;
            }
            return num;
        }

        // groupNumをセットする
        int setGroupNum() {
            assert nextG != null;
            Item p = this;
            int i;
            for(i = 1;;i++) {
                p = p.next;
                if (p.nextG != null)
                    break;
            }
            groupNum = i;
            return groupNum;
        }


        // グループのサイズによって分割、結合する
        void organize() {
            assert nextG != null;
            if (groupNum > SPLIT_NUM) { // 分割
                Item p = this;
                int num = groupNum / 2;
                for(int i = 0; i < num; i++) {
                    p = p.next;
                }
                p.nextG = nextG; // group昇格
                p.prevG = this;
                nextG.prevG = p;
                nextG = p;
                p.groupNum = groupNum - num;
                groupNum = num;
                //assert(groupNum == setGroupNum());
                //assert(p.groupNum == p.setGroupNum());
            }
            else if (nextG != this && groupNum + nextG.groupNum < JOIN_NUM) {
                groupNum += nextG.groupNum;
                Item n = nextG;
                n.nextG.prevG = this;
                nextG = n.nextG;
                n.nextG = n.prevG = null;
            }
        }

        // 同じループのグループか?
        boolean sameLoopG(Item g) {
            assert nextG != null;
            assert g.nextG != null;
            if (this == g)
                return true;
            Item p = nextG;
            for(;;) {
                if (p == this)
                    return false;
                if (p == g)
                    return true;
                p = p.nextG;
            }
        }
    }

    void swap(Item p0, Item p1, int[] lcnt) {
        //log.printf("swap %d %d\n",  p0.num, p1.num);
        Item g0 = p0.getGroup();
        Item g1 = p1.getGroup();
        boolean merge = !g0.sameLoopG(g1);

        if (merge) { // 結合
            int lnum0 = g0.getLoopNum();
            int lnum1 = g1.getLoopNum();

            g0.nextG.prevG = g1;
            g1.nextG.prevG = g0;
            Item t = g0.nextG;
            g0.nextG = g1.nextG;
            g1.nextG = t;

            t = p0.next;
            p0.next = p1.next;
            p1.next = t;

            g0.setGroupNum();
            g1.setGroupNum();
            g0.organize();
            if (g1.nextG != null)
                g1.organize();

//            if (g0.nextG != null)
//                g0.setGroupNum();
//            if (g1.nextG != null)
//                g1.setGroupNum();
//            int gnumAfter = p0.getLoopNum();
            int lnumAfter = lnum0 + lnum1;
            //log.printf("loopnum = (%d %d)-(%d)\n", lnum0, lnum1, lnumAfter);
            lcnt[lnum0]--;
            lcnt[lnum1]--;
            lcnt[lnumAfter]++;
            //log.printf("%s\n", p0.getGroup());
        }
        else {
            Item t;
            // 分割
            //int gnum0 = g0.getLoopNum();
            if (g0 == g1) {
                // 同一グループの分割
                Item p = g0;
                for(;;p = p.next) {
                    if (p == p0)
                        break;
                    if (p == p1) {
                        t = p0;
                        p0 = p1;
                        p1 = t;
                        break;
                    }
                }
                p1.nextG = p1; // group昇格
                p1.prevG = p1;

                t = p0.next;
                p0.next = p1.next;
                p1.next = t;
                g0.setGroupNum();
                p1.setGroupNum();
                int lnum0After = g0.getLoopNum();
                int lnum1After = p1.getLoopNum();
                int lnum0 = lnum0After + lnum1After;
                //log.printf("loopnum = (%d)-(%d %d)\n", lnum0, lnum0After, lnum1After);
                lcnt[lnum0]--;
                lcnt[lnum0After]++;
                lcnt[lnum1After]++;
                //log.printf("%s : %s\n", g0.getGroup(), p1.getGroup());
            }
            else {
                g0.nextG.prevG = g1;
                g1.nextG.prevG = g0;
                t = g0.nextG;
                g0.nextG = g1.nextG;
                g1.nextG = t;

                t = p0.next;
                p0.next = p1.next;
                p1.next = t;

                g0.setGroupNum();
                g1.setGroupNum();
                g0.organize();
                if (g1.nextG != null)
                    g1.organize();
//                g0.organize();
//                if (g1.nextG != null)
//                    g1.organize();
//                g0.setGroupNum();
//                if (g1.nextG != null)
//                    g1.setGroupNum();
                int lnum0After = p0.getLoopNum();
                int lnum1After = p1.getLoopNum();
                int lnum0 = lnum0After + lnum1After;
                //log.printf("loopnum = (%d)-(%d %d)\n", lnum0, lnum0After, lnum1After);
                lcnt[lnum0]--;
                lcnt[lnum0After]++;
                lcnt[lnum1After]++;
                //log.printf("%s : %s\n", p0.getGroup(), p1.getGroup());
            }
        }
    }

    Item[] all;

    void init(int N) {
        all = new Item[N + 1];
//        lcnt = new int[N + 1];
//       lcnt[1] = N;
        for(int i = 1; i <= N; i++) {
            Item item = new Item();
            all[i] = item;
            item.num = i;
            item.next = item;
            item.nextG = item;
            item.prevG = item;
            item.groupNum = 1;
        }

//        swap(all[1], all[2]);
//        swap(all[1], all[3]);
//        log.printf("%s\n",  all[1]);
//        swap(all[2], all[3]);
//        log.printf("%s\n",  all[1]);
//        all[1].connect(all[2]);
//        all[1].connect(all[3]);
//        all[2].connectR(all[4]);
//        System.out.printf("%s %d\n", all[1].treeToString(), all[1].getRoot().loopNum);


    }

    long gcm(long a, long b) {

        while(b != 0) {
            long t = a % b;
            a = b;
            b = t;
        }
        return a;
    }

    int[] S;

    int change;
    void loop(int x, int y, int[] lcnt) {
        int p = S[x];
        int xc;
        int yc = 0;
        for(xc = 1;; xc++) {
            if (p == x)
                break;
            if (p == y)
                yc = xc;
            p = S[p];
        }
        if (yc != 0) { // 分割
            if (--lcnt[xc] == 0) { change |= 2; lcntSet.remove(xc); }
            if (yc      > 1 && ++lcnt[yc     ] == 1) { change |= 1; lcntSet.add(yc); }
            if (xc - yc > 1 && ++lcnt[xc - yc] == 1) { change |= 1; lcntSet.add(xc-yc); }
        }
        else {
            p = S[y];
            for(yc = 1;; yc++) {
                if (p == y)
                    break;
                p = S[p];
            }
            // 結合
            if (xc > 1 && --lcnt[xc] == 0) { change |= 2; lcntSet.remove(xc); }
            if (yc > 1 && --lcnt[yc] == 0) { change |= 2; lcntSet.remove(yc); }
            if (++lcnt[xc + yc] == 1)  { change |= 1; lcntSet.add(xc+yc); }
        }
    }

    long calcLcm(int N, int[] lcnt, long[] pp) {
        int ppnum = 1;
        pp[0] = 1;
        for(int m = 2; m <= N; m++) {
            if (lcnt[m] > 0) {
                int mm = m;
                for(int j = 0; j < ppnum; j++) {
                    mm /= gcm(mm, pp[j]);
                    if (mm == 1)
                        break;
                }
                pp[ppnum - 1] *= mm;
                if (pp[ppnum - 1] > 100000) {
                    pp[ppnum++] = 1;
                }
                if (m > N / 2)
                    break;
            }
        }
        long p = 1;
        for(int j = 0; j < ppnum; j++) {
            p = p * pp[j];
            p = p % 1000000007;
        }
        return p;
    }

    BigInteger BIG = BigInteger.valueOf(1000000007);

    long calcLcm2(int N, int[] lcnt) {
        BigInteger P = BigInteger.ONE;
        for(int m = 2; m <= N; m++) {
            if (lcnt[m] > 0) {
                //System.out.printf("%d ", m);
                BigInteger M = BigInteger.valueOf(m);
                M = M.divide(P.gcd(M));
                P = P.multiply(M);
                if (m > N / 2)
                    break;
            }
        }
        P = P.mod(BIG);
        long p = P.longValue();
        return p;
    }
    long calcLcm3(Set<Integer> lcntSet) {
        BigInteger P = BigInteger.ONE;
        for(Integer m: lcntSet) {
            //System.out.printf("%d ", m);
            BigInteger M = BigInteger.valueOf(m);
            M = M.divide(P.gcd(M));
            P = P.multiply(M);
        }
        P = P.mod(BIG);
        long p = P.longValue();
        return p;
    }

    Set<Integer> lcntSet = new TreeSet<Integer>();

    boolean main() throws IOException {

        Scanner sc = new Scanner(systemin);

        int N = sc.nextInt();
        int Q = sc.nextInt();
        int[] x = new int[Q];
        int[] y = new int[Q];
        for(int i = 0; i < Q; i++) {
            x[i] = sc.nextInt();
            y[i] = sc.nextInt();
            //log.printf("%d %d\n", x[i], y[i]);
        }

        init(N);

        S = new int[N + 1];
        for(int i = 1; i <= N; i++) {
            S[i] = i;
        }

        // cnt別loop数
        int[] lcnt = new int[N + 1];
        lcnt[1] = N;

        long[] pp = new long[100];
        long p = 1;
        for(int q = 0; q < Q; q++) {
            if (false) {
                swap(all[x[q]], all[y[q]], lcnt);
                change = 1;
            }
            else {
                loop(x[q], y[q], lcnt);
                int t = S[x[q]];
                S[x[q]] = S[y[q]];
                S[y[q]] = t;
            }

            if (change != 0) {
                //p = calcLcm(N, lcnt, pp);
                //p = calcLcm2(N, lcnt);  // 2倍で12->22なので ここでほとんどかかっている
                p = calcLcm3(lcntSet);
            }
            result.printf("%d\n", p);
        }

        sc.close();
        return false;
    }

    PrintStream log;
    PrintStream result = System.out;
    BufferedReader systemin;

    static Main instance = new Main();

    Main() {
        systemin = new BufferedReader(new InputStreamReader(System.in));
        log = new PrintStream(new OutputStream() { public void write(int b) {} } );
    }

    public static void main(String[] args) throws IOException {

        instance.main();

        instance.systemin.close();
    }


}