[Luogu P3704] [BZOJ 4816] [SDOI2017]数字表格

洛谷传送门
BZOJ传送门

题目描述

Doris刚刚学习了fibonacci数列。用 f [ i ] f[i] f[i]表示数列的第 i i i项,那么

f [ 0 ] = 0 , f [ 1 ] = 1 f[0]=0,f[1]=1 f[0]=0,f[1]=1,

f [ n ] = f [ n − 1 ] + f [ n − 2 ] , n ≥ 2 f[n]=f[n-1]+f[n-2],n\geq 2 f[n]=f[n1]+f[n2],n2

Doris用老师的超级计算机生成了一个 n × m n×m n×m的表格,

i i i行第 j j j列的格子中的数是 f [ gcd ⁡ ( i , j ) ] f[\gcd(i,j)] f[gcd(i,j)],其中 gcd ⁡ ( i , j ) \gcd(i,j) gcd(i,j)表示 i , j i,j i,j的最大公约数。

Doris的表格中共有 n × m n×m n×m个数,她想知道这些数的乘积是多少。

答案对 1 0 9 + 7 10^9+7 109+7取模。

输入输出格式

输入格式:

有多组测试数据。

第一个一个数 T T T,表示数据组数。

接下来 T T T行,每行两个数 n , m n,m n,m

输出格式:

输出 T T T行,第 i i i行的数是第 i i i组数据的结果

输入输出样例

输入样例#1:
3
2 3
4 5
6 7
输出样例#1:
1
6
960

说明

10 % 10\% 10%的数据, 1 ≤ n , m ≤ 100 1\leq n,m\leq 100 1n,m100

30 % 30\% 30%的数据, 1 ≤ n , m 1\leq n,m 1n,m

另外存在 30 % 30\% 30%的数据, T ≤ 3 T\leq 3 T3

100 % 100\% 100%的数据, T ≤ 1000 , 1 ≤ n , m ≤ 1 0 6 T\leq1000,1\leq n,m\leq 10^6 T1000,1n,m106

解题分析

∏ i = 1 n ∏ j = 1 m F [ g c d ( i , j ) ] = ∏ d = 1 n F [ d ] ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = d ] = ∏ d = 1 n F [ d ] ∑ p = 1 ⌊ n d ⌋ μ ( p ) ⌊ n d p ⌋ ⌊ m d p ⌋ \prod_{i=1}^{n}\prod_{j=1}^mF[gcd(i,j)] \\ =\prod_{d=1}^{n}F[d]^{\sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)=d]} \\ =\prod_{d=1}^{n}F[d]^{\sum_{p=1}^{\lfloor\frac{n}{d}\rfloor}\mu(p)\lfloor\frac{n}{dp}\rfloor\lfloor\frac{m}{dp}\rfloor} i=1nj=1mF[gcd(i,j)]=d=1nF[d]i=1nj=1m[gcd(i,j)=d]=d=1nF[d]p=1dnμ(p)dpndpm

套路地设 T = d p T=dp T=dp
∏ d = 1 n F [ d ] ∑ p = 1 ⌊ n d ⌋ μ ( p ) ⌊ n d p ⌋ ⌊ m d p ⌋ = ∏ T = 1 n ∏ d ∣ T F [ d ] μ ( T d ) ⌊ n T ⌋ ⌊ m T ⌋ = ∏ T = 1 n ∏ d ∣ T ( F [ d ] μ ( T d ) ) ⌊ n T ⌋ ⌊ m T ⌋ \prod_{d=1}^{n}F[d]^{\sum_{p=1}^{\lfloor\frac{n}{d}\rfloor}\mu(p)\lfloor\frac{n}{dp}\rfloor\lfloor\frac{m}{dp}\rfloor} \\ =\prod_{T=1}^{n}\prod_{d|T}F[d]^{\mu(\frac{T}{d})\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor} \\ =\prod_{T=1}^{n}\prod_{d|T}(F[d]^{\mu(\frac{T}{d})})^{\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor} d=1nF[d]p=1dnμ(p)dpndpm=T=1ndTF[d]μ(dT)TnTm=T=1ndT(F[d]μ(dT))TnTm
然后就可以筛出 μ ( i ) \mu(i) μ(i) O ( N l n ( N ) ) O(Nln(N)) O(Nln(N))预处理, O ( T N l o g ( N ) ) O(T\sqrt N log(N)) O(TN log(N))回答询问了。

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MOD 1000000007ll
#define ll long long 
#define MX 1000500
template <class T>
IN void in(T &x)
{
    x = 0; R char c = gc;
    for (; !isdigit(c); c = gc);
    for (;  isdigit(c); c = gc)
    x = (x << 1) + (x << 3) + c - 48;
}
template <class T> IN T max(T a, T b) {return a > b ? a : b;}
template <class T> IN T min(T a, T b) {return a < b ? a : b;}
IN void add(R int ad, int &tar) {tar += ad; if (tar > MOD) tar -= MOD;}
int pcnt;
int miu[MX], pri[MX];
ll sum[MX], F[MX], inv[MX];
bool npr[MX];
IN ll fpow(R ll base, R ll tim)
{
    ll ret = 1;
    W (tim)
    {
        if (tim & 1) ret = ret * base % MOD;
        base = base * base % MOD, tim >>= 1;
    }
    return ret;
}
void get()
{
    miu[1] = 1; R int tar;
    for (R int i = 2; i <= 1e6; ++i)
    {
        if (!npr[i]) pri[++pcnt] = i, miu[i] = -1;
        for (R int j = 1; j <= pcnt; ++j)
        {
            tar = i * pri[j];
            if (tar > 1e6) break;
            npr[tar] = true;
            if (!(i % pri[j])) {miu[tar] = 0; break;}
            miu[tar] = -miu[i];
        }
    }
    sum[0] = F[1] = 1;
    for (R int i = 2; i <= 1e6; ++i) F[i] = (F[i - 1] + F[i - 2]) % MOD;
    for (R int i = 1; i <= 1e6; ++i) inv[i] = fpow(F[i], MOD - 2), sum[i] = 1;
    for (R int i = 1; i <= 1e6; ++i)
    for (R int j = 1, tar; (tar = j * i) <= 1e6; ++j)
    {
        if (miu[j] == 1) sum[tar] = sum[tar] * F[i] % MOD;
        else if (miu[j] == -1) sum[tar] = sum[tar] * inv[i] % MOD;
    }
    for (R int i = 2; i <= 1e6; ++i) sum[i] = sum[i - 1] * sum[i] % MOD;
}
int main(void)
{
    ll ans;
    int T, n, m; in(T); get();
    W (T--)
    {
        in(n), in(m); ans = 1;
        if (n > m) std::swap(n, m);
        for (R int lef = 1, rig; lef <= n; lef = rig + 1)
        {
            rig = std::min(n / (n / lef), m / (m / lef));
            ans = ans * fpow(sum[rig] * fpow(sum[lef - 1], MOD - 2) % MOD, 1ll * (n / lef) * (m / lef)) % MOD;
        }
        printf("%lld\n", ans);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值