Asia Tsukuba 2016-2017 I - Skinny Polygon

Asia Tsukuba 2016-2017 I - Skinny Polygon

Skinny Polygon
题意:给出宽高分别为xbb,ybb的矩形,要求一个顶点在格点的三角形或四边形(可以凹四边形)的面积小于25000,同时满足矩形各边上皆有点

二维平面上,三点坐标分别为 ( 0 , 0 ) , ( a , b ) , ( c , d ) (0,0), (a, b), (c, d) (0,0),(a,b),(c,d)的三角形的面积可以表示为 ∣ a d − b c ∣ 2 \frac{|ad-bc|}{2} 2adbc (叉积的应用)

格点多边形面积公式: S = 内 部 点 数 + 边 界 点 数 2 − 1 S = 内部点数+\frac{边界点数}{2} - 1 S=+21 (皮克定理)

一些特殊情况:

  1. 当xbb和ybb互质的时候,根据扩展欧几里得原理,对于 a = x b b , b = y b b a=xbb,b = ybb a=xbb,b=ybb , 有: ∣ a d − b c ∣ = 1 |ad-bc| = 1 adbc=1

    即存在三角形面积为 1 2 \frac{1}{2} 21, 符合条件

  2. x b b = = y b b xbb==ybb xbb==ybb的时候, 即最外面这个大框为正方形时,取 ( 0 , 0 ) , ( 1 , 1 ) , ( x b b − 1 , y b b ) , ( x b b , y b b − 1 ) (0,0),(1, 1),(xbb-1, ybb),(xbb, ybb-1) (0,0),(1,1),(xbb1,ybb),(xbb,ybb1)四个点构成的凹四边形面积为1

考虑将上述两种特殊情况扩展到一般,

  • 对于情况1,直接还是扩欧,存在 ∣ a d − b c ∣ = g c d ( a , b ) |ad-bc| = gcd(a,b) adbc=gcd(a,b) 即三角形面积为 g c d ( a , b ) 2 \frac{gcd(a, b)}{2} 2gcd(a,b)

  • 对于情况2,考虑仍取 ( 0 , 0 ) , ( x b b − 1 , y b b ) , ( x b b , y b b − 1 ) (0,0), (xbb-1, ybb),(xbb, ybb-1) (0,0),(xbb1,ybb),(xbb,ybb1) 这样三个点,剩下一个点取在矩形对角线最接近 ( 0 , 0 ) (0,0) (0,0) 的格点上,即为 ( x b b g , y b b g ) (\frac{xbb}{g}, \frac{ybb}{g}) (gxbb,gybb) ,凹四边形面积为 x b b g + y b b g 2 \frac{\frac{xbb}{g} +\frac{ ybb}{g}}{2} 2gxbb+gybb (皮克定理或转化为三角形叉积)

在这两种情况中取较小便满足条件

扩展欧几里得 求ax+by=gcd(a,b)的解

要求 a d − b c = g c d ( a , b ) ad - bc = gcd(a, b) adbc=gcd(a,b)
A = a ( x b b ) , B = b ( y b b ) , X = d , Y = − c A X + B Y = g c d ( A , B ) k x = B / g c d k y = A / g c d 通 解 X = X ′ + k x × n , Y = Y ′ + k y × n A = a(xbb), B = b(ybb), X = d, Y= -c\\ AX+BY = gcd(A, B)\\ kx = B /gcd\\ ky = A/gcd\\ 通解 X = X' + kx \times n, Y = Y' + ky \times n A=a(xbb),B=b(ybb),X=d,Y=cAX+BY=gcd(A,B)kx=B/gcdky=A/gcdX=X+kx×n,Y=Y+ky×n

好像直接求出来就满足,所以代码中这段替换其实不知道对不对(?

#include "bits/stdc++.h"

using namespace std;

typedef long long ll;

ll ex_gcd(ll a, ll b, ll &x, ll &y) {
    if (b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    ll d = ex_gcd(b, a % b, x, y);
    ll temp = x;
    x = y;
    y = temp - a / b * y;
    return d;
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

    ll T;
    cin >> T;
    while (T--) {
        ll x, y;
        cin >> x >> y;
        ll xx, yy;
        ll d = ex_gcd(x, y, xx, yy);
        if ((x / d + y / d) / 2 <= 25000) {
            cout << 4 << endl;
            cout << 0 << " " << 0 << endl;
            cout << x - 1 << " " << y << endl;
            cout << x / d << " " << y / d << endl;
            cout << x << " " << y - 1 << endl;
            continue;
        }
        ll kx = -y / d;
        ll ky = -x / d;
//        cerr << xx << " " << yy << " " << kx << " " << ky << endl;
        ll ansx = xx, ansy = yy;
        ll i = 0;
        while (1) {
            if ((xx + kx * i) * (yy + ky * i) <= 0
                && (abs(xx + kx * i) <= y) && (abs(yy + ky * i) <= x)) {
                ansx = xx + kx * i, ansy = yy + ky * i;
                break;
            }
            if ((xx - kx * i) * (yy - ky * i) <= 0
                && (abs(xx -kx * i) <= y) && (abs(yy - ky * i) <= x)) {
                ansx = xx - kx * i, ansy = yy - ky * i;
                break;
            }
            ++i;
        }
        cout << 3 << endl;
        cout << 0 << " " << 0 << endl;
        cout << x << " " << y << endl;
        cout << abs(ansy) << " " << abs(ansx) << endl;
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值