[SDOI2017]数字表格 --- 套路反演

[SDOI2017]数字表格

由于使用markdown的关系
我无法很好的掌控格式,见谅

对于这么简单的一道题竟然能在洛谷混到黑,我感到无语

\(\begin{align*} \prod\limits^{n}_{i=1} \prod\limits^{m}_{j=1} fi[gcd(i,j)] &= \prod\limits^{n}_{d=1} fi[d]^{\sum\limits_{e=1}^{n} [n/de][m/de]\mu(e)} \\ &= \prod\limits^{n}_{T = 1} (\prod\limits_{d|T} fi[d]^{\mu(T/d)})^{[n/T][m/T]} \end{align*}\)

两步化完式子后
只要预处理函数\(f\prod\limits_{d|n} fi[d]^{\mu(n/d)}\)的前缀积就行
可以做到\(O(n)\)\(fi\)\(O(n)\)\(\mu\),枚举因子\(O(n logn)\)求这个函数,\(O(n)\)计算前缀积
为了方便,我们同时求出他们的逆元即可

复杂度\(O(n logn + T\sqrt n \log n)\)

代码

#include <cstdio>
#include <iostream>
#define sid 1000050
#define ll long long
#define mod 1000000007
#define ri register int
using namespace std;

const int N = 1000000;
ll fi[sid], f[sid], iv[sid];
int mu[sid], pr[sid], nop[sid], pp, tot;

int read() { scanf("%d", &pp); return pp; }

ll qpow(ll a, ll k) {
    ll ret = 1;
    while(k) {
        if(k & 1) ret = (ret * a) % mod;
        a = (a * a) % mod; k >>= 1;
    }
    return ret;
}

void Get_Fib() {
    fi[1] = 1; fi[2] = 1;
    for(ri i = 3; i <= N; i ++)
    fi[i] = (fi[i - 1] + fi[i - 2]) % mod;
}

void Get_Mu() {
    mu[1] = 1;
    for(ri i = 2; i <= N; i ++) {
        if(!nop[i]) { pr[++ tot] = i; mu[i] = -1; }
        for(ri j = 1; j <= tot; j ++) {
            int h = i * pr[j];
            if(h > N) break; nop[h] = 1;
            if(i % pr[j] == 0) { mu[h] = 0; break; }
            else mu[h] = -mu[i];
        }
    }
}

void Get_f() {
    for(ri i = 1; i <= N; i ++) f[i] = 1;
    for(ri i = 1; i <= N; i ++) {
        ll inv = qpow(fi[i], mod - 2);
        for(ri j = i; j <= N; j += i)
        if(mu[j / i] == -1) f[j] = (f[j] * inv) % mod;
        else if(mu[j / i]) f[j] = (f[j] * fi[i]) % mod;
    }
    f[0] = 1; iv[0] = 1;
    for(ri i = 1; i <= N; i ++) f[i] = (f[i] * f[i - 1]) % mod;
    for(ri i = 1; i <= N; i ++) iv[i] = qpow(f[i], mod - 2);
}

ll Solve(int n, int m) {
    ll ret = 1;
    if(n > m) swap(n, m);
    for(ri i = 1, j; i <= n; i = j + 1) {
        j = min(n / (n / i), m / (m / i));
        ret = ret * qpow(f[j] * iv[i - 1] % mod, 1ll * (n / i) * (m / i)) % mod;
    }
    return ret;
}

int main() {
    Get_Fib(); Get_Mu(); Get_f();
    int Tt = read();
    while(Tt --) {
        int n = read(), m = read();
        printf("%lld\n", Solve(n, m));
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/reverymoon/p/9189232.html