バイトの競プロメモ

主に競技プログラミング

D - LCM Rush AtCoder Beginner Contest 020

D - LCM Rush
問題概略
1~Nについて、lcm(i,K)を足し合わせた余りを求めよ。

解法
約数は高々1400個しか無いのでgcd(i,k)の値で複数の物を纏められる。
gcd(a,b)=1の時、lcm(a,b) = a * b / gcd(a,b) より bと互いに素であるiの合計 * b / となり、iの合計は包除原理を用いて解ける。
gcd(a,b)がcの際はgcd(a/c,b/c)で同様に求められる。

 public static void solve() throws Exception {
        //longを忘れるなオーバーフローするぞ
        N = ni();
        K = ni();
        setPrimes();
        ArrayList<Integer> div = new ArrayList<>();
        for (int i = 1; i * i <= K; i++) {
            if (K % i == 0) {
                div.add(i);
                if (i * i != K) div.add(K / i);
            }
        }
        long res = 0;
        for (Integer gcd : div) {
            //                            sum ( N / gcd , K / gcd) * gcd / gcd * K
            long par = modMul(lcmRush(N / gcd, K / gcd), K);
            res = modSum(res, par);
        }
        System.out.println(res);
    }

    static ArrayList<Integer> factorization(int n) {
        if (primes == null) setPrimes();
        ArrayList<Integer> fact = new ArrayList<>();
        for (int p : primes) {
            if (n % p == 0) fact.add(p);
            while (n % p == 0) n /= p;
            if (n == 1) break;
        }
        if (n != 1) fact.add(n);
        return fact;
    }

    public static long lcmRush(int n, int k) {
        ArrayList<Integer> fact = factorization(k);
        int fsize = fact.size();
        long sum = 0;
        //包除原理
        for (int m = 0; m < (1 << fsize); m++) {
            long va = 1;
            for (int i = 0; i < fsize; i++) {
                if (bitGet(m, i)) va *= fact.get(i);
            }
            long s = va;
            long t = n / va * va;
            long kosu = n / va;
            // long part = modMul(s + t) * kosu / 2;
            long part = modDiv(modMul(modSum(s, t), kosu), 2);
            if (Integer.bitCount(m) % 2 == 1) sum = modDiff(sum, part);
            else sum = modSum(sum, part);
        }
        return sum;
    }