洛谷P3704 [SDOI2017]数字表格(预处理筛+莫比乌斯反演+乘法逆元+数论分块)

洛谷P3704 [SDOI2017]数字表格(预处理筛+莫比乌斯反演+乘法逆元+数论分块)

题目链接:https://www.luogu.com.cn/problem/P3704
题目大意:给定一组 n , m n,m n,m ,求 ∏ i = 1 n ∏ j = 1 m f ( gcd ⁡ ( i , j ) )   m o d   1 e 9 + 7 \prod\limits_{i=1}^n\prod\limits_{j=1}^mf(\gcd(i,j))\space mod\space 1e9+7 i=1nj=1mf(gcd(i,j)) mod 1e9+7 ,其中 f ( x ) f(x) f(x) 为斐波那契数列的第 x x x 项。
题解:这次变成了求函数的连乘积,看上去无从下手,实际上还是与求函数的连加和一样的套路。不妨设 n ≤ m n\leq m nm

原式: ∏ i = 1 n ∏ j = 1 m f ( gcd ⁡ ( i , j ) ) \prod\limits_{i=1}^n\prod\limits_{j=1}^mf(\gcd(i,j)) i=1nj=1mf(gcd(i,j))

= ∏ i = 1 n ∏ j = 1 m ∏ d = 1 n f ( d ) [ gcd ⁡ ( i , j ) = d ] =\prod\limits_{i=1}^n\prod\limits_{j=1}^m\prod\limits_{d=1}^nf(d)^{[\gcd(i,j)=d]} =i=1nj=1md=1nf(d)[gcd(i,j)=d]

= ∏ d = 1 n ∏ i = 1 n ∏ j = 1 m f ( d ) [ gcd ⁡ ( i , j ) = d ] =\prod\limits_{d=1}^n\prod\limits_{i=1}^n\prod\limits_{j=1}^mf(d)^{[\gcd(i,j)=d]} =d=1ni=1nj=1mf(d)[gcd(i,j)=d]

= ∏ d = 1 n ∏ i = 1 ⌊ n d ⌋ ∏ j = 1 ⌊ m d ⌋ f ( d ) ε ( gcd ⁡ ( i , j ) ) =\prod\limits_{d=1}^n\prod\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}\prod\limits_{j=1}^{\lfloor\frac{m}{d}\rfloor}f(d)^{\varepsilon (\gcd(i,j))} =d=1ni=1dnj=1dmf(d)ε(gcd(i,j))

= ∏ d = 1 n ∏ i = 1 ⌊ n d ⌋ ∏ j = 1 ⌊ m d ⌋ f ( d ) ∑ t ∣ i , t ∣ j μ ( t ) =\prod\limits_{d=1}^n\prod\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}\prod\limits_{j=1}^{\lfloor\frac{m}{d}\rfloor}f(d)^{\sum\limits_{t\mid i,t\mid j}\mu(t)} =d=1ni=1dnj=1dmf(d)ti,tjμ(t)

= ∏ d = 1 n ∏ i = 1 ⌊ n d ⌋ ∏ j = 1 ⌊ m d ⌋ ∏ t ∣ i , t ∣ j f ( d ) μ ( t ) =\prod\limits_{d=1}^n\prod\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}\prod\limits_{j=1}^{\lfloor\frac{m}{d}\rfloor}{\prod\limits_{t\mid i,t\mid j}}f(d)^{\mu(t)} =d=1ni=1dnj=1dmti,tjf(d)μ(t)

= ∏ d = 1 n ∏ t = 1 ⌊ n d ⌋ ∏ i = 1 ⌊ n d t ⌋ ∏ j = 1 ⌊ m d t ⌋ f ( d ) μ ( t ) =\prod\limits_{d=1}^n\prod\limits_{t=1}^{\lfloor\frac{n}{d}\rfloor}\prod\limits_{i=1}^{\lfloor\frac{n}{dt}\rfloor}\prod\limits_{j=1}^{\lfloor\frac{m}{dt}\rfloor}f(d)^{\mu(t)} =d=1nt=1dni=1dtnj=1dtmf(d)μ(t)

T = d t T=dt T=dt ,那么则可化为:

⇒ ∏ T = 1 n ∏ i = 1 ⌊ n T ⌋ ∏ j = 1 ⌊ m T ⌋ ∏ d ∣ T f ( d ) μ ( T d ) \Rightarrow\prod\limits_{T=1}^n\prod\limits_{i=1}^{\lfloor\frac{n}{T}\rfloor}\prod\limits_{j=1}^{\lfloor\frac{m}{T}\rfloor}\prod\limits_{d\mid T}f(d)^{\mu(\frac{T}{d})} T=1ni=1Tnj=1TmdTf(d)μ(dT)

u ( x ) = ∏ d ∣ x f ( d ) μ ( x d ) u(x)=\prod\limits_{d\mid x}f(d)^{\mu(\frac{x}{d})} u(x)=dxf(d)μ(dx) ,显然,这个函数可以使用埃式筛提前进行预处理,埃式筛可以遍历每一个数的每一个非1因数,而 μ ( x ) \mu(x) μ(x) f ( x ) f(x) f(x) 也都能提前进行预处理。则经过预处理后,求任意 u ( x ) u(x) u(x) 的值是 O ( 1 ) O(1) O(1) 的。
此时原式等于:

⇒ ∏ T = 1 n ∏ i = 1 ⌊ n T ⌋ ∏ j = 1 ⌊ m T ⌋ u ( T ) \Rightarrow\prod\limits_{T=1}^n\prod\limits_{i=1}^{\lfloor\frac{n}{T}\rfloor}\prod\limits_{j=1}^{\lfloor\frac{m}{T}\rfloor}u(T) T=1ni=1Tnj=1Tmu(T)

= ∏ T = 1 n u ( T ) ⌊ n T ⌋ ⌊ m T ⌋ =\prod\limits_{T=1}^nu(T)^{{\lfloor\frac{n}{T}\rfloor}{\lfloor\frac{m}{T}\rfloor}} =T=1nu(T)TnTm

即可应用数论分块对原式进行计算加速。这样的话我们需要维护 u ( x ) u(x) u(x) 的前缀积 p u ( x ) pu(x) pu(x) ,当计算一个区间 [ l , r ] [l,r] [l,r] 的场合,只需计算 p u ( r ) / p u ( l − 1 ) pu(r)/pu(l-1) pu(r)/pu(l1) 。但这时便引申出求乘法逆元的问题。对于质数 1 e 9 + 7 1e9+7 1e9+7 ,求出 [ 1 , 1 e 9 + 6 ] [1,1e9+6] [1,1e9+6] 的所有逆元显然是不现实的,不妨先线性求出 [ 1 , 1 e 6 ] [1,1e6] [1,1e6] 的所有逆元,然后这个区间外的所有数的逆元便可以根据线性递推公式求出了,这种做法会比使用费马小定理直接求逆元要快。同时根据费马小定理,有 u ( T ) ⌊ n T ⌋ ⌊ m T ⌋ ≡ u ( T ) ⌊ n T ⌋ ⌊ m T ⌋   m o d   1 e 9 + 6   m o d   1 e 9 + 7 u(T)^{{\lfloor\frac{n}{T}\rfloor}{\lfloor\frac{m}{T}\rfloor}}\equiv u(T)^{{\lfloor\frac{n}{T}\rfloor}{\lfloor\frac{m}{T}\rfloor}\space mod \space 1e9+6}\space mod\space 1e9+7 u(T)TnTmu(T)TnTm mod 1e9+6 mod 1e9+7 ,可以对算法起到一点加速作用。
下面是AC代码。

#include<iostream>
#include<vector>
#include<unordered_map>
using namespace std;
#define debug(x) cerr<<#x<<":"<<x<<endl
#define debug2(x,y) cerr<<#x<<":"<<x<<"  "<<#y<<":"<<y<<endl
#define debug3(x,y,z) cerr<<#x<<":"<<x<<"  "<<#y<<":"<<y<<"  "<<#z<<":"<<z<<endl
typedef long long ll;
struct fastio{
    fastio(){
        ios::sync_with_stdio(0);
        cin.tie(0);
        cout.tie(0);
    }
}fio;
ll N;
const ll M=1e9+7;
const ll PHIM=M-1;
ll qpow(ll x,ll y,ll mod){
    ll res=1;
    x%=mod;
    while(y){
        if(y&1) res=(res*x)%mod;
        x=(x*x)%mod;
        y>>=1;
    }
    return res;
}
vector<int> prime,part,mu,f,inv_M,u,pu;
ll inv(ll x){
    x%=M;
    if(x<N) return inv_M[x];
    return ((ll)(M-(M/x))*(ll)inv(M%x))%M;
}
void init(){
    inv_M=f=part=vector<int>(N,0);
    u=pu=vector<int>(N,1);
    mu=vector<int>(N,-1);
    inv_M[1]=f[1]=mu[1]=1;
    for(ll i=2;i<N;++i){
        f[i]=((ll)f[i-1]+(ll)f[i-2])%M;
        inv_M[i]=((ll)(M-(M/i))*(ll)inv_M[M%i])%M;
        if(!part[i]){
            prime.push_back(i);
            part[i]=i;
        }
        for(auto j:prime){
            if(i*j>=N) break;
            part[i*j]=j;
            if(i%j==0){
                mu[i*j]=0;
                break;
            }
            else mu[i*j]=-mu[i];
        }
    }
    for(int i=2;i<N;++i){
        for(int j=i;j<N;j+=i){
            if(mu[j/i]==-1) u[j]=(ll)u[j]*inv(f[i])%M;
            if(mu[j/i]==1) u[j]=(ll)u[j]*f[i]%M;
        }
        pu[i]=(ll)u[i]*pu[i-1]%M;
    }
}
ll g(ll x,ll y){
    if(x>y) swap(x,y);
    ll res=1;
    for(ll l=3,r;l<=x;l=r+1){
        r=min(x/(x/l),y/(y/l));
        ll t=qpow((pu[r]*inv(pu[l-1])),((x/l)*(y/l))%PHIM,M);
        res=res*t%M;
    }
    return res;
}
signed main(){
    int t,n,m,maxx=-1;
    cin>>t;
    vector<pair<int,int> >data;
    while(t--){
        cin>>n>>m;
        data.push_back({n,m});
        maxx=max(maxx,max(n,m));
    }
    N=maxx;
    init();
    for(auto i:data) cout<<g(i.first,i.second)<<endl;
}

第一次做出洛谷黑题,用了一天时间,可喜可贺。。。个人感觉 u ( x ) u(x) u(x) 也可以用线性筛预处理,可惜本蒟蒻想不出来。。。
艰辛地AC了

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值