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

时间:2023-03-09 16:45:17
【BZOJ4816】【SDOI2017】数字表格 [莫比乌斯反演]

数字表格

Time Limit: 50 Sec  Memory Limit: 128 MB
[Submit][Status][Discuss]

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

Output

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

Sample Input

  3
  2 3
  4 5
  6 7

Sample Output

  1
  6
  960

HINT

  T<=1000,1<=n,m<=10^6

Solution

  运用莫比乌斯反演,得到式子:

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

  这样我们对于内外分块即可,复杂度为O(n^(0.75)*T)。

  然后我们会发现在BZOJ上过不去,怎么办呢?卡常!BearChild运用了如下的卡常技巧:

    1.  读入优化; 2. O(n)预处理逆元; 3. 内嵌汇编实现乘和取模; 4. 记录n/i,避免多次除法。

Code

 #include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
using namespace std;
typedef long long s64; const int ONE = 1e6+;
const int MOD = 1e9+;
const int PHI = 1e9+; int T;
int n,m;
int prime[ONE],p_num,miu[ONE];
int F[ONE];
bool isp[ONE];
s64 Ans; int get()
{
int res=,Q=; char c;
while( (c=getchar())< || c>)
if(c=='-')Q=-;
if(Q) res=c-;
while((c=getchar())>= && c<=)
res=res*+c-;
return res*Q;
} int Quickpow(int a,int b)
{
int res = ;
while(b)
{
if(b&) res = (s64)res*a%MOD;
a = (s64)a*a%MOD;
b>>=;
}
return res;
} void Deal_first(int MaxN)
{
F[]=;
F[]=; for(int i=; i<=MaxN; i++) F[i] = ((s64)F[i-]+F[i-]) % MOD;
F[]=; for(int i=; i<=MaxN; i++) F[i] = (s64)F[i]*F[i-] % MOD; miu[] = ;
for(int i=; i<=MaxN; i++)
{
if(!isp[i])
prime[++p_num] = i, miu[i] = -;
for(int j=; j<=p_num, i*prime[j]<=MaxN; j++)
{
isp[i * prime[j]] = ;
if(i % prime[j] == )
{
miu[i * prime[j]] = ;
break;
}
miu[i * prime[j]] = -miu[i];
}
miu[i] += miu[i-];
}
} int f(int n,int m)
{
if(n > m) swap(n,m);
s64 Ans = ;
for(int i=,j=; i<=n; i=j+)
{
j = min(n/(n/i), m/(m/i));
Ans += (s64)(n/i) * (m/i)%PHI * ((s64)(miu[j] - miu[i-] + PHI)%PHI) % PHI;
Ans %= PHI;
}
return Ans;
} void Solve()
{
n=get(); m=get();
if(n > m) swap(n,m);
Ans = ;
for(int i=,j=; i<=n; i=j+)
{
j = min(n/(n/i), m/(m/i));
Ans = Ans * Quickpow( (s64)F[j] * Quickpow(F[i-],MOD-) % MOD , f(n/i,m/i) % PHI) % MOD;
}
printf("%lld\n",Ans);
} int main()
{
Deal_first(ONE-);
T = get();
while(T--)
Solve();
return ;
}

非卡常版

 #include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
using namespace std;
typedef long long s64; const int ONE = 1e6+;
const int MOD = 1e9+;
const int PHI = 1e9+; int T;
int n,m;
int prime[ONE],p_num,miu[ONE];
int Niyu[ONE];
int F[ONE];
bool isp[ONE];
int Ans; inline int get()
{
int res=,Q=; char c;
while( (c=getchar())< || c>)
if(c=='-')Q=-;
if(Q) res=c-;
while((c=getchar())>= && c<=)
res=res*+c-;
return res*Q;
} inline int modmul(const int &a, const int &b,const int &M)
{
int ret;
__asm__ __volatile__("\tmull %%ebx\n\tdivl %%ecx\n" : "=d"(ret) : "a"(a), "b"(b), "c"(M));
return ret;
} inline int Quickpow(int a,int b)
{
int res = ;
while(b)
{
if(b&) res = modmul(res,a,MOD);
a = modmul(a,a,MOD);
b>>=;
}
return res;
} inline void Deal_first(int MaxN)
{
F[]=; F[]=;
int val=;
for(int i=; i<=MaxN; i++)
{
F[i] = F[i-]+F[i-];
if(F[i] >= MOD) F[i] -= MOD;
val = modmul(val,F[i],MOD);
}
Niyu[MaxN] = Quickpow(val, MOD-);
for(int i=MaxN-;i>=;i--) Niyu[i] = modmul(Niyu[i+],F[i+],MOD);
Niyu[] = Niyu[]; F[]=;
for(int i=; i<=MaxN; i++) F[i] = modmul(F[i],F[i-],MOD); miu[] = ;
for(int i=; i<=MaxN; i++)
{
if(!isp[i])
prime[++p_num] = i, miu[i] = -;
for(int j=; j<=p_num, i*prime[j]<=MaxN; j++)
{
isp[i * prime[j]] = ;
if(i % prime[j] == )
{
miu[i * prime[j]] = ;
break;
}
miu[i * prime[j]] = -miu[i];
}
miu[i] += miu[i-];
}
} inline int f(int n,int m)
{
if(n > m) swap(n,m);
int Ans = ;
for(int i=,j=; i<=n; i=j+)
{
int x=n/i, y=m/i;
j = min(n/x, m/y);
Ans = ((s64)Ans + modmul(modmul(x,y,PHI) , ((s64)miu[j] - miu[i-] + PHI), PHI) )%PHI;
}
return Ans;
} inline void Solve()
{
n=get(); m=get();
if(n > m) swap(n,m);
Ans = ;
for(int i=,j=; i<=n; i=j+)
{
int x=n/i, y=m/i;
j = min(n/x, m/y);
Ans = (s64)modmul(Ans , Quickpow( modmul(F[j],Niyu[i-],MOD) , f(x,y)), MOD);
}
printf("%d\n",Ans);
} int main()
{
Deal_first(ONE-);
T = get();
while(T--)
Solve();
return ;
}

卡常版