题目:
题解:
不愧是一去不返的SDOI,就是套路
我们要求的是
∏i=1n∏j=1mf((i,j))=∏i=1n∏j=1m∑d=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=1∑ndj=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=1n∏d|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);
}
}

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

被折叠的 条评论
为什么被折叠?



