CF1548D2 Gregor and the Odd Cows (Hard)
https://www.luogu.com.cn/problem/CF1548D2
弱化版不屑于写(bushi)
然而VP的时候还是没打出来
要知道几个比较重要的结论
- 皮克定理:
S
=
a
+
b
2
−
1
S=a+\frac{b}{2}-1
S=a+2b−1
a是内部的点,b是边上的点 - g c d ( x , y ) m o d 4 = g c d ( x m o d 4 , y m o d 4 ) gcd(x,y) \mod 4 = gcd(x\mod4,y\mod4) gcd(x,y)mod4=gcd(xmod4,ymod4)
- S = x 1 ( y 2 − y 3 ) + x 2 ( y 3 − y 1 ) + x 3 ( y 1 − y 2 ) 2 S=\frac{x_1(y_2-y_3)+x_2(y_3-y_1)+x_3(y_1-y_2)}{2} S=2x1(y2−y3)+x2(y3−y1)+x3(y1−y2)
- 两个点连线在格点上的数量 g c d ( x 1 − x 2 , y 1 − y 2 ) gcd(x_1-x_2,y_1-y_2) gcd(x1−x2,y1−y2)
- 面积为整数的三角形一定是三条边上的点数必须是 三个偶数或两奇一偶(根据皮克定理)
然后就可以先把所有的点缩到4以内,然后对于每个点枚举另外两个点统计即可
对于每个点记
c
n
t
[
x
]
[
y
]
[
p
]
cnt[x][y][p]
cnt[x][y][p]表示在模4意义下,另一个点的坐标,这条边上的点个数为p点的 边个数
然后再枚举另一条边统计即可
细节不多,思维难度有点大
code:
#include<bits/stdc++.h>
#define ll long long
#define N 6005
using namespace std;
int gcd(int x, int y) {
return y? gcd(y, x % y) : x;
}
inline int calc(int a, int b, int c, int d, int e, int f) {
return (((a * (d - f) + c * (f - b) + e * (b - d)) % 4 + 4) / 2) % 4;
}
int n, x[N], y[N], cnt[5][5][5];
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i ++) scanf("%d%d", &x[i], &y[i]);
ll ans1 = 0, ans2 = 0;
for(int i = 1; i <= n; i ++) {
for(int j = 1; j <= n; j ++) if(j != i)
cnt[x[j] % 4][y[j] % 4][gcd(abs(x[i] - x[j]), abs(y[i] - y[j])) % 4] ++;
for(int a = 0; a < 4; a ++)//枚举第一条边的点
for(int b = 0; b < 4; b ++)
for(int p = 0; p < 4; p ++) //边上的个数
if(cnt[a][b][p]) {
int ret = cnt[a][b][p] --;
for(int c = a&1; c < 4; c += 2)
for(int d = b&1; d < 4; d += 2) //枚举第二个点(另一条边)
for(int q = p&1; q < 4; q += 2)
if(cnt[c][d][q]) {
int S = calc(x[i] % 4, y[i] % 4, a, b, c, d), pq = gcd(abs(a - c), abs(b - d));
// printf("%d %d %d %d (%d,%d) (%d,%d) %d\n", S, p, q, pq, a, b, c, d, i);
if((S - (p + q + pq) / 2 + 1) & 1) {
if(p & 1) ans1 += 1ll * ret * cnt[c][d][q];
else ans2 += 1ll * ret * cnt[c][d][q];
}
}
cnt[a][b][p] ++;
}
for(int a = 0; a < 4; a ++)
for(int b = 0; b < 4; b ++)
for(int p = 0; p < 4; p ++)
cnt[a][b][p] = 0;
// printf("%lld %lld\n", ans1, ans2);
}
printf("%lld", ans1 / 2 + ans2 / 6);
return 0;
}