[BZOJ4816][Sdoi2017]数字表格 数学

来源:互联网 发布:php进阶书籍推荐知乎 编辑:程序博客网 时间:2024/05/17 09:32

考虑每个fi对答案的贡献,就能得到式子

k=1nfd|kμ(d)nkdmkdk

转化成枚举kd,令T=kd
T=1n(d|Tfμ(Td)d)nTmT

预处理小括号内的部分,令gT=d|Tfμ(Td)d
单次询问只需要回答
T=1ngnTmTT

易证nTmT只有O(N)种取值,分段计数即可
预处理O(NN),询问O(TNlog2N),log来自快速幂

#include <bits/stdc++.h>#define mod 1000000007#define N 1000050#define tp 1000000using namespace std;typedef long long LL;inline int rd() {int r;scanf("%d",&r);return r;}bool np[N];int eu[N],pr[N],g[N],fib[N],sum[N],siv[N],cnt,n,m,ans;inline int qp(int a,long long b) {    int ret = 1;    while (b) {        if (b&1) ret = 1LL * ret * a % mod;        b >>= 1, a = 1LL * a * a % mod;    }    return ret;}void preset() {    eu[1] = 1;    for (int i=2;i<=tp;i++) {        if (!np[i]) pr[++cnt] = i, eu[i] = -1;        for (int j=1;j<=cnt && pr[j]*i<=tp;j++) {            np[i*pr[j]] = 1;            if (i%pr[j] == 0) {                eu[i*pr[j]] = 0;                break;            }            eu[i*pr[j]] = -eu[i];        }    }       fib[0] = 0, fib[1] = 1;    for (int i=2;i<=tp;i++) fib[i] = (fib[i-1] + fib[i-2]) % mod;    for (int i=1;i<=tp;i++) g[i] = 1;       sum[0] = siv[0] = 1;    for (int i=1;i<=tp;++i) {        int cur = fib[i];        int inv = qp(cur, mod-2);        for (int j=1;i*j<=tp;++j) {            if (eu[j] == 0) continue;            g[i*j] = eu[j] == 1 ?                1LL * g[i*j] * cur % mod:                1LL * g[i*j] * inv % mod;        }        sum[i] = 1LL * sum[i-1] * g[i] % mod;        siv[i] = 1LL * siv[i-1] * qp(g[i], mod-2) % mod;    }}void solve() {    n = rd(), m = rd(), ans = 1;    if (n>m) swap(n,m);    for (int i=1,lst,cur;i<=n;i=lst+1) {        lst = min(n/(n/i), m/(m/i));        cur = 1LL * sum[lst] * siv[i-1] % mod;        ans = 1LL * ans * qp(cur, 1LL*(n/i)*(m/i)) % mod;    }    printf("%d\n",ans);}int main() {    freopen("product.in","r",stdin);    freopen("product.out","w",stdout);    preset();    for (int T=rd();T;T--) solve();    return 0;}
原创粉丝点击