您的位置:首页 > 其它

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

2018-03-27 19:16 363 查看

题目:

我是超链接

题解:

不愧是一去不返的SDOI,就是套路

我们要求的是∏i=1n∏j=1mf((i,j))=∏i=1n∏j=1m∑d=1n[(i,j)=d]f(d)∏i=1n∏j=1mf((i,j))=∏i=1n∏j=1m∑d=1n[(i,j)=d]f(d)

后面之所以是连加因为只会有一个d满足条件,要是累乘会变成0,接着画

∏d=1nf(d)∑ndi=1∑ndj=1[(i,j)=1]∏d=1nf(d)∑i=1nd∑j=1nd[(i,j)=1]

诶为什么是角标的累加呢?因为对于这个约数d,f(d)出现了累加次数

这样一看角标就很套路了,我们直接画到最简形式

∏d=1nf(d)∑ndt=1ndtmdtμ(t)∏d=1nf(d)∑t=1ndndtmdtμ(t)

我们看到了dt,这东西可不好枚举,这也是套路啊,设T=dt并且枚举T

∏T=1n∏d|Tf(d)μ(Td)nTmT∏T=1n∏d|Tf(d)μ(Td)nTmT

这个nTmTnTmT显然可以分块优化,那么我们现在就是要求∏d|Tf(d)μ(Td)∏d|Tf(d)μ(Td)的前缀积,然后可以把除法转化为逆元进行计算,这东西也可以在筛子里求出来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
,pri
,tot;LL g
,inv
,f
;bool ss
;
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);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: