BZOJ题目传送门
洛谷题目传送门
题目要我们求 2∑ni=1∑mj=1(i,j)−nm 2 ∑ i = 1 n ∑ j = 1 m ( i , j ) − n m
主要是求 ∑ni=1∑mj=1(i,j) ∑ i = 1 n ∑ j = 1 m ( i , j )
我们设 f(x)=∑ni=1∑mj=1[(i,j)=x],F(x)=∑ni=1∑mj=1[x|(i,j)] f ( x ) = ∑ i = 1 n ∑ j = 1 m [ ( i , j ) = x ] , F ( x ) = ∑ i = 1 n ∑ j = 1 m [ x | ( i , j ) ]
那么原式 =∑min(n,m)x=1x∗f(x) = ∑ x = 1 m i n ( n , m ) x ∗ f ( x )
很容易发现 F(x)=⌊nx⌋⌊mx⌋ F ( x ) = ⌊ n x ⌋ ⌊ m x ⌋
有两种做法通过 F(x) F ( x ) 求 f(x) f ( x ) :
一:乱搞。发现 f(x)=F(x)−f(kx)(k>1) f ( x ) = F ( x ) − f ( k x ) ( k > 1 ) ,那么从后往前推求出 f(x) f ( x ) 即可。
二:反演。套公式可得 f(x)=∑x|dF(d)μ(d/x) f ( x ) = ∑ x | d F ( d ) μ ( d / x ) ,直接枚举即可。
代码:
//乱搞
#include
#include
#define N 100005
using namespace std;
typedef long long LL;
LL f[N],ans,n,m;
int main(){
scanf("%lld%lld",&n,&m);
if (n>m) swap(n,m);
for (int i=n;i;i--){
f[i]=(n/i)*(m/i);
for (int j=i<<1;j<=n;j+=i) f[i]-=f[j];
ans+=((i<<1)-1)*f[i];
}
return printf("%lld\n",ans),0;
}
//反演
#include
#include
#include
#define N 100005
using namespace std;
typedef long long LL;
int n,m,mu[N],p[N];
LL ans;
bool f[N];
inline void mkp(){
mu[1]=1;
for (int i=2;i<=n;i++){
if (!f[i]) p[++p[0]]=i,mu[i]=-1;
for (int j=1,v;j<=p[0]&&(v=i*p[j])<=n;j++){
f[v]=true,mu[v]=-mu[i];
if (!(i%p[j])) { mu[v]=0; break; }
}
}
}
int main(){
scanf("%d%d",&n,&m);
if (n>m) swap(n,m); mkp();
for (int i=1;i<=n;i++)
for (int j=i;j<=n;j+=i)
ans+=1ll*mu[j/i]*(1ll*n/(1ll*j))*(1ll*m/(1ll*j))*((i<<1)-1);
return printf("%lld\n",ans),0;
}