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+2b1
    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(y2y3)+x2(y3y1)+x3(y1y2)
  • 两个点连线在格点上的数量 g c d ( x 1 − x 2 , y 1 − y 2 ) gcd(x_1-x_2,y_1-y_2) gcd(x1x2,y1y2)
  • 面积为整数的三角形一定是三条边上的点数必须是 三个偶数或两奇一偶(根据皮克定理)

然后就可以先把所有的点缩到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;
}
posted @ 2021-08-31 18:18  lahlah  阅读(34)  评论(0编辑  收藏  举报