[BZOJ4816][SDOI2017]数字表格(反演)

本文解析了一道SDOI竞赛中的数学题目,通过巧妙地转换和简化公式,最终得到了高效的求解算法。文中详细介绍了从原始问题到求解过程中的关键步骤,并提供了完整的C++代码实现。

题目:

我是超链接

题解:

不愧是一去不返的SDOI,就是套路
我们要求的是

i=1nj=1mf((i,j))=i=1nj=1md=1n[(i,j)=d]f(d) ∏ i = 1 n ∏ j = 1 m f ( ( i , j ) ) = ∏ i = 1 n ∏ j = 1 m ∑ d = 1 n [ ( i , j ) = d ] f ( d )

后面之所以是连加因为只会有一个d满足条件,要是累乘会变成0,接着画
d=1nf(d)ndi=1ndj=1[(i,j)=1] ∏ d = 1 n f ( d ) ∑ i = 1 n d ∑ j = 1 n d [ ( i , j ) = 1 ]

诶为什么是角标的累加呢?因为对于这个约数d,f(d)出现了累加次数
这样一看角标就很套路了,我们直接画到最简形式
d=1nf(d)ndt=1ndtmdtμ(t) ∏ d = 1 n f ( d ) ∑ t = 1 n d n d t m d t μ ( t )

我们看到了dt,这东西可不好枚举,这也是套路啊,设T=dt并且枚举T
T=1nd|Tf(d)μ(Td)nTmT ∏ T = 1 n ∏ d | T f ( d ) μ ( T d ) n T m T

这个 nTmT n T m T 显然可以分块优化,那么我们现在就是要求 d|Tf(d)μ(Td) ∏ d | T f ( d ) μ ( T d ) 的前缀积,然后可以把除法转化为逆元进行计算,这东西也可以在筛子里求出来 O(n) O ( n )
然后就结束咯~

代码:

#include <cstdio>
#include <iostream>
#define LL long long
using namespace std;
const int mod=1e9+7;
const int maxn=1e6;
const int N=1000005;
int mu[N],pri[N],tot;LL g[N],inv[N],f[N];bool ss[N];
LL ksm(LL a,LL k)
{
    LL ans=1;
    for (;k;k>>=1,a=a*a%mod)
      if (k&1) ans=ans*a%mod;
    return ans;
}
void init()
{
    g[0]=f[1]=g[1]=mu[1]=inv[1]=1;
    for (int i=2;i<=maxn;i++)
    {
        f[i]=(f[i-1]+f[i-2])%mod;
        inv[i]=ksm(f[i],mod-2);g[i]=1;
        if (!ss[i]) pri[++tot]=i,mu[i]=-1;
        for (int j=1;j<=tot && pri[j]*i<=maxn;j++)
        {
            ss[pri[j]*i]=1;
            if (i%pri[j]==0) break;
            mu[pri[j]*i]=-mu[i];    
        }
    }
    for (int i=1;i<=maxn;i++)//d
      {
        for (int j=i;j<=maxn;j+=i)//T
          if (mu[j/i]==-1) g[j]=g[j]*inv[i]%mod;
          else if (mu[j/i]==1) g[j]=g[j]*f[i]%mod;
      }
    for (int i=2;i<=maxn;i++) g[i]=g[i-1]*g[i]%mod;
}
int main()
{
    init();int T,n,m;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d%d",&n,&m);if (n>m) swap(n,m);
        LL ans=1;
        for (int i=1,last;i<=n;i=last+1)
        {
            last=min(n/(n/i),m/(m/i));
            ans=ans*ksm(g[last]*ksm(g[i-1],mod-2)%mod,(LL)(n/i)*(LL)(m/i)%(mod-1))%mod;
        }
        printf("%lld\n",ans);
    }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值