Codeforces 136D (220D) Little Elephant and Triangle
問題
(x, y)(0≦x≦w, 0≦y≦h)を満たす点3点からなる三角形で、
面積が正の整数であるものの個数を求めよ。
ただし、点の順序は区別するものとする。
制約条件
w, h≦4000
方針
(0, 0), (a, b), (c, d)を結んでできる三角形の面積は、|ad - bc| / 2.
これが整数になればよいので、
(x, y)が偶数、奇数かで点を分類して数えておいて、
それぞれのパリティごとに点の個数を掛け合わせればよい。
そのままだと面積が0の点も含んでしまうので、以下の場合にわけて取り除く。
- 1点または2点に縮退している場合
- y軸またはx軸に平行な直線上に三点が並ぶ場合
- 軸に平行でない直線上に三点が並ぶ場合
ソースコード
const int mod = (int)1e9 + 7; int h, w; ll cnt[2][2]; ll P(int n, int m){ ll res = 1; rep(i, m) res = res * (n - i) % mod; return res; } ll tri(ll n){ ll res = n; rep(i, 2) res = res * n % mod; return res; } int main(){ cin >> h >> w; ll ans = 0; rep(i, h + 1) rep(j, w + 1){ cnt[i % 2][j % 2]++; if(i && j){ int c = __gcd(i, j); //3点が軸に平行でない一直線上にある場合 //順番6通りを無視し、かつ下にいくものを数えていないので、本来は12倍 //視点が(h - i + 1) * (w - i + 1)通り ans += mod - 12ll * (c - 1) * (h - i + 1) * (w - j + 1) % mod; } } ans += mod - (w + 1) * P(h + 1, 3) % mod; //縦にならぶ ans += mod - (h + 1) * P(w + 1, 3) % mod; //横にならぶ ans += P((h + 1ll) * (w + 1), 3) % mod; //こことこの下の行で、 ans += mod - tri((w + 1ll) * (h + 1)); //一点か二点に縮退している場合 rep(a, 2) rep(b, 2) rep(c, 2) rep(d, 2) rep(e, 2) rep(f, 2){ int aa = c - a, bb = d - b; int cc = e - a, dd = f - b; //パリティが条件を満たしているなら数える if((aa * dd - bb * cc) % 2 == 0) ans += cnt[a][b] * cnt[c][d] % mod * cnt[e][f] % mod; } cout << ans % mod << endl; return 0; }