1. 程式人生 > >cf Educational Codeforces Round 50 E. Covered Points

cf Educational Codeforces Round 50 E. Covered Points

原題: E. Covered Points time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard output You are given n segments on a Cartesian plane. Each segment’s endpoints have integer coordinates. Segments can intersect with each other. No two segments lie on the same line.

Count the number of distinct points with integer coordinates, which are covered by at least one segment.

Input The first line contains a single integer n (1≤n≤1000) — the number of segments.Each of the next n lines contains four integers Axi,Ayi,Bxi,Byi (−10^ 6≤Axi,Ayi,Bxi,Byi≤10^6) — the coordinates of the endpoints A, B (A≠B) of the i-th segment. It is guaranteed that no two segments lie on the same line.

Output Print a single integer — the number of distinct points with integer coordinates, which are covered by at least one segment.

Examples input 9 0 0 4 4 -1 5 4 0 4 0 4 4 5 2 11 2 6 1 6 7 5 6 11 6 10 1 10 7 7 0 9 8 10 -1 11 -1 output 42 input 4 -1 2 1 2 -1 0 1 0 -1 0 0 3 0 3 1 0 output 7

中文:

給你n條線段,問你這n條線段上所包含的整數點一共有多少。

程式碼:

#include <bits/stdc++.h>
#define Vector Point
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pll;

struct Point
{
    ll x,y;
    Point(ll x=0,ll y=0):x(x),y(y){}
};
bool operator < (const Point &a,const Point &b)//排序用,按照x的座標從小到大,如果x相同,那麼按照y從小到大
{
    return a.x<b.x||(a.x==b.x&&a.y<b.y);
}
Vector operator +(Vector A,Vector B){return Vector(A.x+B.x,A.y+B.y);}//座標點相加
Vector operator -(Vector A,Vector B){return Vector(A.x-B.x,A.y-B.y);}//相減

double Cross(Vector A,Vector B)
{
    return A.x*B.y-A.y*B.x;
}


bool in(ll x,ll l,ll r)
{
    if(l>r)
        swap(l,r);
    return l<=x&&x<=r;
}
Point GetCrossPoint(Point p1,Point p2,Point p3,Point p4,bool &mark)
{
    ll a1,b1,c1,a2,b2,c2,D;
    a1=p1.y-p2.y;
    b1=p2.x-p1.x;
    //c1=p1.x*p2.y-p1.y*p2.x;
    c1=-a1*p1.x-b1*p1.y;

    a2=p3.y-p4.y;
    b2=p4.x-p3.x;
    //c2=p3.x*p4.y-p3.y*p4.x;
    c2=-a2*p3.x-b2*p3.y;


    D=a1*b2-a2*b1;

    Point res;

    if(D==0)
    {
        mark=0;
        return res;
    }
    if((b1*c2-b2*c1)%D)
    {
        mark=0;
        return res;
    }
    res.x=(b1*c2-b2*c1)/D;
    if((c1*a2-c2*a1)%D)
    {
        mark=0;
        return res;
    }
    res.y=(c1*a2-c2*a1)/D;

    if(!in(res.x,p1.x,p2.x)||!in(res.y,p1.y,p2.y))
        mark=0;
    if(!in(res.x,p3.x,p4.x)||!in(res.y,p3.y,p4.y))
        mark=0;


    return res;
}
int n;
Point pois[1001][2];

ll gcd(ll x,ll y)
{
    if(y==0)
        return x;
    else
        return gcd(y,x%y);
}

int main()
{
    ios::sync_with_stdio(false);
    while(cin>>n)
    {
        for(int i=1;i<=n;i++)
        {
            cin>>pois[i][0].x>>pois[i][0].y>>pois[i][1].x>>pois[i][1].y;
        }

        set<pll> sp;
        ll ans=0;
        bool mark;
        for(int i=1;i<=n;i++)
        {
            ans+=gcd(llabs(pois[i][0].x-pois[i][1].x),llabs(pois[i][0].y-pois[i][1].y))+1;

            sp.clear();
            for(int j=1;j<i;j++)
            {
                mark=1;
                Point p=GetCrossPoint(pois[i][0],pois[i][1],pois[j][0],pois[j][1],mark);
                if(mark)
                {
                       //cout<<i<<" "<<j<<endl;
                       sp.insert(make_pair(p.x,p.y));

                }
            }
            ans-=sp.size();
        }
        cout<<ans<<endl;
    }
    return 0;
}

思路:

首先考慮一條線段上有多少個整數點? 答案是gcd(abs(x1-x2),abs(y1-y2))+1個

挑戰程式設計競賽那本書上就有這個例題,其實很好理解,假設這個線段所跨越的橫座標可以分成a份,跨越的縱座標可以分成b份,那麼要想分成整數份,就是a和b的最大公約數。

接下來要出去重複點的,由於只有1000個線段,直接列舉兩條線段,判斷兩條線段是否相交,將所有相交的整數點儲存在一個set當中,然後讓最後答案減去每次set中元素的個數就是最後結果。

注意程式精度和除零的問題,程式碼參考了官方題解。