bzoj3505: [Cqoi2014]数三角形
题目链接
题解
首先n * m的网格图点数为 (n + 1) * (m + 1)
那么n ++,m++
所有的方案\(C(3,n + m)\)减去在同一行的\(m * C(3,n)\)减去在同一列的\(n * C(3,m)\)减去斜着的
枚举两个点,两点间的点数为\(gcd(x1,y1) - gcd(x2,y2)\)个,但复杂度不能接受
因为每条直线的起点终点都在矩形上,我们可以(n + m) * (n + m)枚举四条边上的点,C(k,3)统计
*枚举一个点的坐标,它与(0,0)显然构成了唯一的一条线段,然后我们发现这条线段其实可以移动,所以就将一条线段的解\(\times (n-x) \times(m-y)\)
代码
#include<cstdio>
#include<algorithm>
#define int long long
inline int read() {
int x = 0 ,f = 1;
char c = getchar() ;
while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar(); }
while(c <= '9' && c >= '0') x = x * 10 + c - '0',c = getchar();
return x * f;
}
const int maxn = 2007;
int n,m;
inline int C(int n) { return n * (n - 1) * (n - 2) / 6; }
struct Node {
int x,y;
Node (int X = 0,int Y = 0) : x(X),y(Y) {};
} a[maxn],b[maxn];
int gcd(int x,int y) {
if(y == 0) return x;
else return gcd(y,x % y);
}
main() {
n = read(),m = read();
n ++ ,m ++;
int ans = 0;
ans += C(n * m);
//printf("%d\n",ans);
ans -= m * C(n);
ans -= n * C(m); int num = 0,num2 = 0;
for(int i = 1;i <= n;++ i)
for(int j = 1;j <= m;++ j)
ans -= 2 * (gcd(i,j) - 1) * (n - i) * (m - j);
//printf("%d\n",ans);
printf("%lld\n",ans);
return 0;
}