「SDOI2017」数字表格

题目链接


问题分析

\[ \begin{aligned} Ans&=\prod_{i=1}^n\prod_{j=1}^mf[\gcd(i,j)]\\ &=\prod_{t=1}^nf(t)^{\sum\limits_{i=1}^n\sum\limits_{j=1}^m[\gcd(i,j)=t]}\\ &=\prod_{t=1}^nf(t)^{\sum\limits_{t|d}^n\mu(\frac{d}{t})\lfloor\frac{n}{d}\rfloor\lfloor\frac{m}{d}\rfloor}\\ &=\prod_{t=1}^n\prod_{t|d}f(t)^{\mu(\frac{d}{t})\lfloor\frac{n}{d}\rfloor\lfloor\frac{m}{d}\rfloor}\\ &=\prod_{d=1}^n\prod_{t|d}f(t)^{\mu(\frac{d}{t})\lfloor\frac{n}{d}\rfloor\lfloor\frac{m}{d}\rfloor}\\ &=\prod_{d=1}^{n}[\prod_{t|d}f(t)^{\mu(\frac{d}{t})}]^{\lfloor\frac{n}{d}\rfloor\lfloor\frac{m}{d}\rfloor} \end{aligned} \]

其中对\(\sum\limits_{i=1}^n\sum\limits_{j=1}^m[\gcd(i,j)=t]\)进行莫比乌斯反演。

\(f(t)=\sum\limits_{i=1}^n\sum\limits_{j=1}^m[\gcd(i,j)=t]\)。设\(F(t)=\sum\limits_{i=1}^n\sum\limits_{j=1}^m[t|\gcd(i,j)]=\lfloor\frac{n}{t}\rfloor\lfloor\frac{m}{t}\rfloor\)

因为\(F(t)=\sum\limits_{t|d}f(d)\),所以\(f(t)=\sum\limits_{t|d}\mu(\frac{d}{t})F(d)=\sum\limits_{t|d}\mu(\frac{d}{t})\lfloor\frac{n}{d}\rfloor\lfloor\frac{m}{d}\rfloor\)

最后的式子中,中括号内的东西可以\(O(n\log n)\)预处理。然后对于每个询问进行整除分块。如果不考虑快速幂,时间复杂度就是\(O(T\sqrt n+n\log n)\)

参考代码

#include <bits/stdc++.h>
#define LL long long
using namespace std;

const LL Maxn = 1000010;
const LL N = 1000000;
const LL Mod = 1000000007;
LL Num, Prime[ Maxn ], Vis[ Maxn ], Mu[ Maxn ], F[ Maxn ], G[ Maxn ];

LL Power( LL x, LL y ) {
    if( y == 0 ) return 1LL;
    LL t = Power( x, y >> 1 );
    t = t * t % Mod;
    if( y & 1 ) t = t * x % Mod;
    return t;
}

void Init() {
    F[ 0 ] = 0; F[ 1 ] = 1;
    for( int i = 2; i <= N; ++i )
        F[ i ] = ( F[ i - 1 ] + F[ i - 2 ] ) % Mod;
    Mu[ 1 ] = 1;
    for( int i = 2; i <= N; ++i ) {
        if( !Vis[ i ] ) {
            Mu[ i ] = -1;
            Prime[ ++Num ] = i;
        }
        for( int j = 1; j <= Num && i * Prime[ j ] <= N; ++j ) {
            Vis[ i * Prime[ j ] ] = 1;
            if( i % Prime[ j ] ) break;
            Mu[ i *Prime[ j ] ] = -Mu[ i ];
        }
    }
    for( int i = 0; i <= N; ++i ) G[ i ] = 1;
    for( int i = 1; i <= N; ++i ) {
        for( int j = i; j <= N; j += i ) {
            if( Mu[ j / i ] == 1 ) 
                G[ j ] = G[ j ] * F[ i ] % Mod;
            if( Mu[ j / i ] == -1 )
                G[ j ] = G[ j ] * Power( F[ i ], Mod - 2 ) % Mod;
        }
    }
    for( int i = 1; i <= N; ++i ) G[ i ] = G[ i ] * G[ i - 1 ] % Mod;
    return;
}

void Work() {
    LL n, m;
    LL Ans = 1;
    scanf( "%lld%lld", &n, &m );
    if( n > m ) swap( n, m );
    for( int i = 1, j; i <= n; i = j + 1 ) {
        j = min( ( n / ( n / i ) ), ( m / ( m / i ) ) );
        Ans = Ans * Power( G[ j ] * Power( G[ i - 1 ], Mod - 2 ) % Mod, ( n / i ) * ( m / i ) % Mod ) % Mod;
    }
    printf( "%lld\n", Ans );
    return;
}

int main() {
    Init();
    int TestCases;
    scanf( "%d", &TestCases );
    for( ; TestCases--; ) Work();
    return 0;
}

转载于:https://www.cnblogs.com/chy-2003/p/10559345.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值