我们枚举斜率j/i gcd(i,j)==1
那么不会造成重复的起点满足 x+i<=n && y+j<=m && (x-i<=0 || y-j<=0)
那么枚举一下
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
return *p1++;
}
inline void read(int &x){
char c=nc(),b=1;
for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
int n,m;
int main(){
int T,Ans=0;
freopen("t.in","r",stdin);
freopen("t.out","w",stdout);
read(T);
while (T--){
read(n); read(m);
Ans=0;
for (int i=1;i<=n/2;i++)
for (int j=1;j<=m/2;j++)
if (__gcd(i,j)==1)
Ans+=m*i+n*j-3*i*j;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (__gcd(i,j)==1)
Ans+=(n-i)*(m-j);
for (int i=1;i<=n/2;i++)
for (int j=1;j<=m/2;j++)
if (__gcd(i,j)==1)
Ans-=(n-i)*(m-j);
// for (int i=1;i<=n;i++)
//for (int j=1;j<=m;j++)
// if (__gcd(i,j)==1)
// Ans+=(m-j)*min(i,n-i)+(n-i)*min(j,m-j)-min(i,n-i)*min(j,m-j);
Ans=Ans*2+n+m;
printf("%d\n",Ans);
}
return 0;
}
正解可以莫比乌斯反演
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
return *p1++;
}
inline void read(int &x){
char c=nc(),b=1;
for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
const int P=1<<30;
const int N=400005;
int mobius[N];
ll sum0[N],sum1[N],sum2[N];
ll Sum[N];
int prime[N],num,vst[N];
inline void Pre(){
const int maxn=400000;
for (int i=2;i<=maxn;i++){
if (!vst[i]) prime[++num]=i,mobius[i]=-1;
for (int j=1;j<=num && (ll)i*prime[j]<=maxn;j++){
vst[prime[j]*i]=1;
if (i%prime[j]==0){
mobius[prime[j]*i]=0;
break;
}
else
mobius[prime[j]*i]=-mobius[i];
}
}
mobius[1]=1;
for (int i=1;i<=maxn;i++){
sum0[i]=(sum0[i-1]+mobius[i]+P)%P;
sum1[i]=(sum1[i-1]+(ll)i*(mobius[i]+P)%P)%P;
sum2[i]=(sum2[i-1]+(ll)i*i%P*(mobius[i]+P)%P)%P;
Sum[i]=(Sum[i-1]+i)%P;
}
}
int n,m; ll Ans;
inline ll IJ(int n,int m){
if (n>m) swap(n,m);
ll ret=0;
for (int i=1,last;i<=n;i=last+1){
last=min(n/(n/i),m/(m/i));
ret+=Sum[n/i]*Sum[m/i]%P*(sum2[last]-sum2[i-1]+P)%P; ret%=P;
}
return ret;
}
inline ll NM(int n,int m){
if (n>m) swap(n,m);
ll ret=0;
for (int i=1,last;i<=n;i=last+1){
last=min(n/(n/i),m/(m/i));
ret+=(n/i)*(m/i)%P*(sum0[last]-sum0[i-1]+P)%P; ret%=P;
}
ret=ret*::n%P*::m%P;
return ret;
}
inline ll MI(int n,int m){
int maxn=min(n,m);
ll ret=0;
for (int i=1,last;i<=maxn;i=last+1){
last=min(n/(n/i),m/(m/i));
ret+=(m/i)*Sum[n/i]%P*(sum1[last]-sum1[i-1]+P)%P; ret%=P;
}
ret=ret*::m%P;
return ret;
}
inline ll NJ(int n,int m){
int maxn=min(n,m);
ll ret=0;
for (int i=1,last;i<=maxn;i=last+1){
last=min(n/(n/i),m/(m/i));
ret+=(n/i)*Sum[m/i]*(sum1[last]-sum1[i-1]+P)%P; ret%=P;
}
ret=ret*::n%P;
return ret;
}
int main(){
int T;
freopen("t.in","r",stdin);
freopen("t.out","w",stdout);
read(T); Pre();
while (T--){
read(n); read(m);
Ans=0;
//Ans+=(MI(n/2,m/2)+NJ(n/2,m/2)+P-3*IJ(n/2,m/2)%P)%P; Ans%=P;
Ans+=(2*MI(n/2,m/2)+2*NJ(n/2,m/2)+P-4*IJ(n/2,m/2)%P+P-NM(n/2,m/2))%P; Ans%=P;
Ans+=(NM(n,m)+IJ(n,m)+P-MI(n,m)+P-NJ(n,m))%P; Ans%=P;
//Ans+=P-(NM(n/2,m/2)+P-MI(n/2,m/2)+P-NJ(n/2,m/2)+IJ(n/2,m/2))%P; Ans%=P;
printf("%lld\n",(Ans*2+n+m)%P);
}
return 0;
}