BZOJ4816 || 洛谷P3704 [SDOI2017]数字表格【莫比乌斯反演】

Time Limit: 50 Sec
Memory Limit: 128 MB

Description

Doris刚刚学习了fibonacci数列。用f[i]表示数列的第i项,那么
f[0]=0,f[1]=1
f[n]=f[n-1]+f[n-2],n>=2
Doris用老师的超级计算机生成了一个n×m的表格,第i行第j列的格子中的数是f[gcd(i,j)],其中gcd(i,j)表示i,
j的最大公约数。Doris的表格中共有n×m个数,她想知道这些数的乘积是多少。答案对10^9+7取模。

Input

有多组测试数据。
第一个一个数T,表示数据组数。
接下来T行,每行两个数n,m
T<=1000,1<=n,m<=10^6

Output

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


题目分析

g ( d ) g(d) g(d)表示 1 ≤ x ≤ n , 1 ≤ y ≤ m 1\leq x\leq n,1\leq y\leq m 1xn,1ym内有多少个 ( x , y ) (x,y) (x,y)满足 g c d ( x , y ) = d gcd(x,y)=d gcd(x,y)=d
那么可以转化目标表达式
∏ i = 1 n ∏ j = 1 m f ( g c d ( i , j ) ) = ∏ d = 1 m i n ( n , m ) f ( d ) g ( d ) \prod_{i=1}^n\prod_{j=1}^mf(gcd(i,j))=\prod_{d=1}^{min(n,m)}f(d)^{g(d)} i=1nj=1mf(gcd(i,j))=d=1min(n,m)f(d)g(d)

这个 g ( d ) g(d) g(d)几乎是莫比乌斯反演考烂了的套路了

g ( d ) = ∑ d ∣ k μ ( k d ) ⌊ n k ⌋ ⌊ m k ⌋ = ∑ t = 1 m i n ( ⌊ n d ⌋ , ⌊ m d ⌋ ) μ ( t ) ⌊ n t d ⌋ ⌊ m t d ⌋ g(d)=\sum_{d|k}\mu(\frac{k}{d})\lfloor\frac{n}{k}\rfloor\lfloor\frac{m}{k}\rfloor=\sum_{t=1}^{min(\lfloor\frac{n}{d}\rfloor,\lfloor\frac{m}{d}\rfloor)}\mu(t)\lfloor\frac{n}{td}\rfloor\lfloor\frac{m}{td}\rfloor g(d)=dkμ(dk)knkm=t=1min(dn,dm)μ(t)tdntdm

带入原式
∏ d = 1 m i n ( n , m ) f ( d ) ∑ d ∣ k μ ( k d ) ⌊ n k ⌋ ⌊ m k ⌋ = ∏ d = 1 m i n ( n , m ) ∏ d ∣ k f ( d ) μ ( k d ) ⌊ n k ⌋ ⌊ m k ⌋ \prod_{d=1}^{min(n,m)}f(d)^{\sum_{d|k}\mu(\frac{k}{d})\lfloor\frac{n}{k}\rfloor\lfloor\frac{m}{k}\rfloor}=\prod_{d=1}^{min(n,m)}\prod_{d|k}f(d)^{\mu(\frac{k}{d})\lfloor\frac{n}{k}\rfloor\lfloor\frac{m}{k}\rfloor} d=1min(n,m)f(d)dkμ(dk)knkm=d=1min(n,m)dkf(d)μ(dk)knkm

交换 k , d k,d k,d的枚举顺序

∏ k = 1 m i n ( n , m ) ∏ d ∣ k f ( d ) μ ( k d ) ⌊ n k ⌋ ⌊ m k ⌋ \prod_{k=1}^{min(n,m)}\prod_{d|k}f(d)^{\mu(\frac{k}{d})\lfloor\frac{n}{k}\rfloor\lfloor\frac{m}{k}\rfloor} k=1min(n,m)dkf(d)μ(dk)knkm

看着似乎有点复杂,把中间那坨抽出来表示
h ( k ) = ∏ d ∣ k f ( d ) μ ( k d ) h(k)=\prod_{d|k}f(d)^{\mu(\frac{k}{d})} h(k)=dkf(d)μ(dk),那么表达式为 ∏ k = 1 m i n ( n , m ) h ( k ) ⌊ n k ⌋ ⌊ m k ⌋ \prod_{k=1}^{min(n,m)}h(k)^{\lfloor\frac{n}{k}\rfloor\lfloor\frac{m}{k}\rfloor} k=1min(n,m)h(k)knkm

预处理 h ( k ) h(k) h(k)前缀积,每个询问用整除分块优化后复杂度 O ( n l o g n ) O(\sqrt{n}logn) O(n logn),这里log是快速幂计算的复杂度
h ( k ) h(k) h(k)可以用枚举倍数的方法预处理,复杂度 O ( n l o g n ) O(nlogn) O(nlogn),莫比乌斯函数预处理 O ( n ) O(n) O(n)
最后总复杂度 O ( n l o g n + n + T n l o g n ) O(nlogn+n+T\sqrt{n}logn) O(nlogn+n+Tn logn)

#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long lt;
  
int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}
 
const lt mod=1e9+7;
const int maxn=1000010;
int T,miu[maxn];
int vis[maxn],prim[maxn],cnt;
lt f[maxn],h[maxn];
lt sum[maxn];
 
lt qpow(lt a,lt k)
{
    lt res=1;
    while(k>0){
        if(k&1) res=(res*a)%mod;
        a=(a*a)%mod; k>>=1;
    }
    return res;
}
 
void calc(int n)
{
    miu[1]=1;
    for(int i=2;i<=n;++i)
    {
        if(!vis[i]) prim[++cnt]=i,miu[i]=-1;
        for(int j=1;j<=cnt;++j)
        {
            if(i*prim[j]>n) break;
            vis[i*prim[j]]=1;
            if(i%prim[j]==0) break;
            else miu[i*prim[j]]=-miu[i];
        }
    }
     
    f[0]=0; f[1]=1;
    for(int i=2;i<=n;++i)
    f[i]=(f[i-1]+f[i-2])%mod;
     
    for(int i=0;i<=n;++i) h[i]=1;
    for(int i=1;i<=n;++i)
    {
        lt inv=1ll*qpow(f[i],mod-2);//先计算f[i]逆元,下面枚举遇到miu为-1时不用重复计算
        for(int j=i;j<=n;j+=i)
        {
            if(miu[j/i]==-1) h[j]=h[j]*inv%mod;
            else if(miu[j/i]==1) h[j]=h[j]*f[i]%mod;
        }
    }
     
    for(int i=1;i<=n;++i)
    h[i]=h[i-1]*h[i]%mod;
}
 
lt query(int n,int m)
{
    lt res=1; int lim=min(n,m);
    for(int ll=1,rr;ll<=lim;ll=rr+1)
    {
        rr=min(n/(n/ll),m/(m/ll));
        lt Mul=h[rr]*qpow(h[ll-1],mod-2)%mod , ki=1ll*(n/ll)*(m/ll);
        res=res*qpow(Mul,ki)%mod;
    }
    return res;
}
 
int main()
{
    T=read(); calc(maxn-5);
    while(T--)
    {
        int n=read(),m=read();
        printf("%lld\n",query(n,m));
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
毕设新目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip 【备注】 1、该资源内目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设目、课程设计、大作业、初期目立演示等。 4、如果基础还行,或热爱钻研,亦可在此目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我! 毕设新目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip
综合小区管理系统管理系统按照操作主体分为管理员和用户。管理员的功能包括报修管理、车位管理、车位分配管理、出入管理、字典管理、房屋管理、物业费缴纳管理、公告管理、物业人员投诉管理、我的私信管理、物业人员管理、用户管理、管理员管理。用户的功能包括管理部门以及部门岗位信息,管理招聘信息,培训信息,薪资信息等。该系统采用了Mysql数据库,Java语言,Spring Boot框架等技术进行编程实现。 综合小区管理系统管理系统可以提高综合小区管理系统信息管理问题的解决效率,优化综合小区管理系统信息处理流程,保证综合小区管理系统信息数据的安全,它是一个非常可靠,非常安全的应用程序。 管理员权限操作的功能包括管理公告,管理综合小区管理系统信息,包括出入管理,报修管理,报修管理,物业费缴纳等,可以管理操作员。 出入管理界面,管理员在出入管理界面中可以对界面中显示,可以对招聘信息的招聘状态进行查看,可以添加新的招聘信息等。报修管理界面,管理员在报修管理界面中查看奖罚种类信息,奖罚描述信息,新增奖惩信息等。车位管理界面,管理员在车位管理界面中新增。公告管理界面,管理员在公告管理界面查看公告的工作状态,可以对公告的数据进行导出,可以添加新公告的信息,可以编辑公告信息,删除公告信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值