(O(nmlogn))
首先,我们处理本题,对于正向思维,即将所有的满足题意的三角形直接数出并不容易实现,我们可以考虑从反面入手,只要从所有的情况中减去不合法的情况即可
对于一条横向的线,不合法的数量为 (C(m+1,3)) ,同理,对于一条竖直的线,不合法的数量即为 (C(n+1,3)) 个
对于一条斜向的线,我们可以从两个点之间的距离入手,考虑这两个点之间不合法情况的数量,对于横向距离为 (i) , 对于纵向距离为 (j) 三角形,一共有 ((m-i+1) imes (n-j+1)) 个,其中每个三角形对于不合法的数量的贡献为: (gcd(i,j)-1) ,同时,我们用这种方式只能处理出斜率非负的情况,再将其乘以 (2) 即可
复杂度: (O(nmlogn))
code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<math.h>
#define ll long long
const ll maxn=1e3+10;
ll n,m;
ll vis[maxn][maxn];
inline ll cal(ll x)
{
if(x<3) return 0;
return x*(x-1)*(x-2)/6;
}
inline ll gcd(ll a,ll b)
{
return b==0 ? a : gcd(b,a%b);
}
int main(void)
{
scanf("%lld %lld",&n,&m);
ll sum=cal((n+1)*(m+1));
sum-=cal(n+1)*(m+1);
sum-=cal(m+1)*(n+1);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
sum-=(n-i+1)*(m-j+1)*(gcd(i,j)-1)*2;
}
}
printf("%lld
",sum);
return 0;
}
(O(n))
我们可以考虑对于斜着的线的计数方案,即:
[displaystyle sum_{i=1}^{n}{displaystyle sum_{j=1}^{m}{(n-i+1)(m-j+1)(gcd(i,j)-1)}}
]
那么,我们可以用 (id) 函数改写为:
[displaystyle sum_{i=1}^{n}{displaystyle sum_{j=1}^{m}{(n-i+1)(m-j+1)[id(gcd(i,j)-1)]}}
]
卷开即得:
[displaystyle sum_{i=1}^{n}{displaystyle sum_{j=1}^{m}{(n-i+1)(m-j+1) displaystyle sum_{d|gcd(i,j)}^{d
eq 1}{varphi(d)}}}
]
变更枚举顺序得:
[displaystyle sum_{d=2}^{min(n,m)}{varphi(d) displaystyle sum_{i=1}^{lfloor{frac{n}{d}}
floor}{(n-i imes d +1 )} displaystyle sum_{j=1}^{lfloor{frac{m}{d}}
floor}{(m-j imes d +1 )}}
]
对于式 (displaystyle sum_{i=1}^{lfloor{frac{n}{d}} floor}{(n-i imes d +1 )}) ,显然即为一个等差数列求和,其首项为 (n-1 imes d+1) ,末项为 (n- lfloor{frac{n}{d}} floor imes d +1) ,则和为
[((n-1 imes d+1)+(n- lfloor{frac{n}{d}}
floor imes d +1)) imes lfloor{frac{n}{d}}
floor imes frac{1}{2}
]
即为:
[{(n-d+(n mod d)+2)} imes lfloor{frac{n}{d}}
floor imes frac{1}{2}
]
故总和为:
[frac{1}{4} displaystyle sum_{d=2}^{min(n,m)}{varphi(d)}{{(n-d+(n mod d)+2)} imes lfloor{frac{n}{d}}
floor}{{(m-d+(m mod d)+2)} imes lfloor{frac{m}{d}}
floor}
]
则线性筛预处理欧拉函数后 (O(n)) 求和即可
code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<math.h>
#include<algorithm>
#define ll long long
const ll maxn=1e3+10;
ll n,m,tot,sum,ans;
ll phi[maxn],vis[maxn],prime[maxn];
inline ll cal(ll x)
{
if(x<3) return 0;
return x*(x-1)*(x-2)/6;
}
inline void pre(ll x)
{
phi[1]=1;
for(int i=2;i<=x;i++)
{
if(!vis[i])
{
vis[i]=1;
prime[++tot]=i;
phi[i]=i-1;
}
for(int j=1;j<=tot&&i*prime[j]<=x;j++)
{
vis[i*prime[j]]=1;
if(i%prime[j]==0)
{
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
}
int main(void)
{
scanf("%lld %lld",&n,&m);
pre(maxn-5);
ans=cal((n+1)*(m+1));
ans-=cal(n+1)*(m+1);
ans-=cal(m+1)*(n+1);
// for(int i=1;i<=100;i++)
// {
// printf("%lld
",phi[i]);
// }
for(int i=2;i<=std::min(n,m);i++)
{
sum+=phi[i]*(n-i+(n%i)+2)*(n/i)*(m-i+(m%i)+2)*(m/i)/2;
}
// printf("%lld
",sum);
printf("%lld
",ans-sum);
}