备注
这题为啥叫wang yu xuan
题目大意
h[i]表示i的质因子的最大指数。现有ca组询问,每组给出n与m,
ans=∑ni=1∑mj=1h[gcd(i,j)]
ca<=10000,n,m<=10000000
繁衍
我们很容易可以筛出h数组。
我们设f[i]表示有多少对gcd为i,则莫比乌斯反演一下,我们易得
ans=∑nd=1h[d]∗∑⌊nd⌋i=1⌊nd∗i⌋∗⌊md∗i⌋∗μ[i]
设T=d*i,更换主体得
ans=∑nT=1⌊nT⌋∗⌊mT⌋∗∑d|Th[d]∗μ[Td]
可以发现后面部分只与T有关,设后面那个为a[T],假设我们能预处理出a数组,就可以用分块解决所有询问。
推导
设b[T]表示T的质因子个数,c[T]表示T的质因子中有多少个指数等于h[T]。
设
T=p1k1∗p2k2...pb[T]kb[T]
那么d显然是从其中每项枚举一个不大于对应指数的指数相乘而得。
如果对于质因子pi,枚举其的指数小于ki-1,那么显然
Td
的莫比乌斯函数值是为0的。
因此我们就知道了,每一项指数都为原指数或原指数减一。
那么根据定义很容易得到h[d]=h[T]或h[T]-1,则我们进行分类讨论。
首先要先了解一个结论:
当j=0时
∑ji=0Cij∗(−1)i=1
当j>0时
∑ji=0Cij∗(−1)i=0
如何证明呢?
我们知道对于
(a−b)j
将其展开后就是
∑ji=0Cij∗(−1)i∗aj−i∗bi
如果我们令a=b=1那么就是
∑ji=0Cij∗(−1)i
而
(1−1)j
,在j=0时为1否则为0。
假如你不知道为什么
(a−b)j=∑ji=0Cij∗(−1)i∗aj−i∗bi
可以这样理解:你在草稿本上竖着写下j个a-b,那么从上往下每一个你都有两种选择:a或-b,所以最后就得证。
同样的我们可以获得一个结论:
∑ji=1Cij∗(−1)i=−1
当h[d]=h[T]-1时:
那么对于所有指数最大的质因子都不能让其指数变为原来的指数,因此答案为
(h[T]−1)∗∑b[T]−c[T]i=0Cib[T]−c[T]∗(−1)b[T]−i
由于
(−1)b[T]−i=(−1)b[T]∗(−1)−i=(−1)b[T]∗(−1)i
所以为
(h[T]−1)∗(−1)b[T]∗∑b[T]−c[T]i=0Cib[T]−c[T]∗(−1)i
根据上面的结论,当b[T]=c[T]时结果为
(h[T]−1)∗(−1)b[T]
否则为0。
当h[d]=h[T]时:
那么我们要至少选一个原来的指数最大的质因子让其指数为原来的指数,其他随便选,答案为
h[T]∗∑c[T]i=1Cic[T]∗∑b[T]−c[T]j=0Cjb[T]−c[T]∗(−1)b[T]−i−j
当b[T]=c[T]时结果为
h[T]∗(−1)b[T]+1
否则为0.
那么得到总结论:
当b[T]=c[T]时结果为
(−1)b[T]+1
否则为0。
因此我们线筛出
μ
,枚举i,如果i的莫比乌斯函数不为0,则
a[i]=−μ[i]
,接下来让i的所有幂的a值都等于a[i]。
参考程序
注意这题只能ans开long long否则会超时。
#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=10000000+10;
int a[maxn],mu[maxn],pri[maxn],num[maxn];
bool bz[maxn];
int i,j,k,l,t,n,m,top,ca;
ll ans;
int main(){
freopen("wyx.in","r",stdin);freopen("wyx.out","w",stdout);
fo(i,2,maxn-10){
if (!bz[i]){
pri[++top]=i;
mu[i]=-1;a[i]=1;
}
fo(j,1,top){
if (i>(maxn-10)/pri[j]) break;
bz[i*pri[j]]=1;
if (i%pri[j]==0){
mu[i*pri[j]]=0;
break;
}
mu[i*pri[j]]=-mu[i];
a[i*pri[j]]=-a[i];
}
}
fo(i,2,maxn-10){
if (mu[i]==0) continue;
j=i;
while (j<=(maxn-10)/i){
a[j*i]=a[i];
j*=i;
}
}
fo(i,1,maxn-10) num[i]=num[i-1]+a[i];
scanf("%d",&ca);
while (ca--){
scanf("%d%d",&n,&m);
if (n>m) swap(n,m);
ans=0;
i=1;
while (i<=n){
j=min(n/(n/i),m/(m/i));
ll x=(n/i),y=(m/i),z=(num[j]-num[i-1]);
ans=ans+x*y*z;
i=j+1;
}
printf("%lld\n",ans);
}
}