不想咕太久..就随便找个题更一下
题意
求题面里那个式子
题解
有一个常用的小式子
$$sum_{x|a,x|b}varphi(x)=gcd(a,b)$$
用这个式子直接对题面的式子进行化简
$$
egin{aligned}
&sum_{i=1}^nsum_{j=1}^n(a_i,a_j)·(i,j)\
&=sum_{i=1}^nsum_{j=1}^n(sum_{x|i,x|j}varphi(x))(a_i,a_j)\
&=sum_{x=1}^nvarphi(x)sum_{x|i}sum_{x|j}(a_i,a_j)
end{aligned}
$$
枚举x,相当于求一个大小为$ frac{n}{x}$的集合内两两$ gcd$的和
再用一次最上面的式子优化
$$
egin{aligned}
&sum_{i=1}^nsum_{j=1}^ngcd(a_i,a_j)\
&=sum_{d=1}^nvarphi(d)(sum_{i=1}^n[d|a_i])^2
end{aligned}
$$
预处理每个数的约数,每次暴力计算
复杂度是对的..跑的飞快...
代码
小范围暴力抢了rk1
#include<ctime> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #include<vector> #define rt register int #define ll long long using namespace std; inline ll read(){ ll x=0;char zf=1;char ch=getchar(); while(ch!='-'&&!isdigit(ch))ch=getchar(); if(ch=='-')zf=-1,ch=getchar(); while(isdigit(ch))x=x*10+ch-'0',ch=getchar();return x*zf; } void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);} void writeln(const ll y){write(y);putchar(' ');} int k,m,n,x,y,z,cnt,ans; #define N 100000 bool pri[N+10];int ss[N+10],phi[N+10]; void init(){ phi[1]=1; for(rt i=2;i<=N;i++){ if(!pri[i]) ss[++cnt]=i,phi[i]=i-1; for(rt j=1;j<=cnt&&i*ss[j]<=N;j++){ phi[i*ss[j]]=phi[i]*phi[ss[j]]; pri[i*ss[j]]=1; if(i%ss[j]==0){ phi[i*ss[j]]=phi[i]*ss[j]; break; } } } } int a[100010],sum[100010]; vector<int>ys[100010]; ll calc2(int x){ ll ret=0; for(rt i=x;i<=n;i+=x)sum[a[i]]++; for(rt i=1;i<=n;i++){ int now=0; for(rt j=i;j<=n;j+=i)now+=sum[j]; ret+=1ll*phi[i]*now*now; } for(rt i=x;i<=n;i+=x)sum[a[i]]=0; return ret; } ll calc(int x){ ll ret=0; for(rt i=x;i<=n;i+=x){ for(auto j:ys[a[i]]){ ret+=(sum[j]*2+1)*phi[j]; sum[j]++; } } for(rt i=x;i<=n;i+=x){ for(auto j:ys[a[i]])sum[j]=0; } return ret; } int main(){ init();n=read(); for(rt i=1;i<=n;i++)a[i]=read(); for(rt i=1;i<=n;i++) for(rt j=i;j<=n;j+=i)ys[j].push_back(i); ll ans=0; for(rt x=1;x<=n;x++){ if(x<=10)ans+=1ll*phi[x]*calc2(x);else ans+=1ll*phi[x]*calc(x); } cout<<ans%1000000007; return 0; }