好题鸭。。
不好直接求三角形个数,那就用全集-补集,转化为求三点共线的数量。
具体求法是求出水平共线数量与竖直共线数量和斜线共线数量。
用排列组合的知识可知为水平和竖直的为$C_n^3$?与$C_m^3?$。
求斜线三点共线:显然,对于点$(a,b) (x,y)$连成的线段$(其中a>x,b>y)$,在它们中间有$gcd(a-x,b-y)-1$个整点,因此基本的思路就是枚举两个点,然后第3个点就是$gcd(a-x,b-y)-1$种可能了。我们又发现,这些线段是可以平移和对称(/和\)的,于是并不需要枚举所有的两个点,只用枚举$(0,0)$和$(x,y)$,然后通过平移和对称($*2$)来计算出现的次数。
那么可以发现,这样任意一条线,向上只能平移$(n-i)$,向下$(m - j)$次,
所以出现次数就为$(n - i + 1) * (m - j + 1)$,其中$+1$是因为可以不移动
#include<iostream> #include<cstdio> #define ll long long #define R register ll using namespace std; inline int g() { R ret=0,fix=1; register char ch; while(!isdigit(ch=getchar())) fix=ch==‘-‘?-1:fix; do ret=ret*10+(ch^48); while(isdigit(ch=getchar())); return ret*fix; } int n,m; ll ans; inline int gcd(int a,int b) {return b?gcd(b,a%b):a;} signed main() { n=g()+1,m=g()+1; ans=1ll*n*m; ans=1ll*ans*(ans-1)*(ans-2)/6-1ll*m*n*(n-1)*(n-2)/6-1ll*n*m*(m-1)*(m-2)/6; for(R i=1;i<n;++i) for(R j=1;j<m;++j) ans-=1ll*2*(gcd(i,j)-1)*(n-i)*(m-j); printf("%lld\n",ans); }
2019.06.01
原文地址:https://www.cnblogs.com/Jackpei/p/10958377.html
时间: 2024-10-07 15:09:37