https://www.luogu.com.cn/problem/P3166
思路:
cf也有一道几乎一致的题,但是那题是2e3个点,可以n^2logn,先算出总数C(n,3),然后预处理经过某个点的斜率的点数和取C(x,2)再减掉。
但是这题不行。
有1e6个点。需要O(nm)的做法。
不过也有O(n)的做法
总体思路也是算出三角形的总数,减去共线的三角形个数,即为答案。
对于总数是C( (n+1)*(m+1),3).
然后有(m+1)*C(n+1,3)和(n+1)*C(m+1,3)
另外一部分就是斜着的。
由于只有n^2的时间,就只能枚举一个点的坐标,根据它与原点的连线,确定线上有多少个坐标点。
首先已知坐标有(0,0)和枚举的(i,j),那么这条直线的解析式为y=j/i*x,因为这是一条线段,所以(0≤x≤i,0≤y≤j),而且我们要排除(0,0)和(i,j)两个点,所以(0<x<i,0<y<j),我们还要使y为整数,那么当i,j互质的时候不满足,所以i,j一定不互质。所以先化简解析式,得y=( j/gcd(i,j) )) / ( i /gcd(i,j) )*x
由于i>x>0,x为i/gcd(i,j)的倍数时候有解。
所以有解的个数为gcd(i,j)-1.
这是一条原点的斜线。还需要平移。
算出能往右平移多少,往上平移多少,乘起来就是整个这样的线段有多少条。(n−i+1)∗(m−j+1)
然后有对称的斜线所以还要乘2.
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=2e5+1000;
typedef long long LL;
inline LL read(){LL x=0,f=1;char ch=getchar(); while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;}
LL C(LL x){
return x*(x-1)*(x-2)/6;
}
int main(void){
cin.tie(0);std::ios::sync_with_stdio(false);
LL n,m;cin>>n>>m;
n++;
m++;
LL ans=C(n*m)-m*C(n)-n*C(m);
for(LL i=1;i<n;i++){
for(LL j=1;j<m;j++){
ans-=2*(__gcd(i,j)-1)*(n-i)*(m-j);
}
}
cout<<ans<<"\n";
return 0;
}