<题目链接>
<转载于 >>> >
题目大意:
在二维平面上给出n条不共线的线段(线段端点是整数),问这些线段总共覆盖到了多少个整数点。
解题分析:
用GCD可求的某条给定线段上有多少个整数点,理由如下:
GCD(n,m)为n与m的最大公约数,通过辗转相除法求得。令g=GCD(n,m); n=x*g, m=y*g.所以将横坐标分为g个x份,将纵坐标分为g个y份。所以,本题线段覆盖的整数点个数为 g+1 (因为包含端点,如果不包含端点就为 g-1 )。
但是这样求的的覆盖点数是包含重复点数的,所以我们可以对每条线段再遍历一次,求的它与它之后线段的不重复交点个数,然后用总的交点个数减去这些重复计算的交点,就是答案了。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<iostream> 5 #include<cmath> 6 #include<vector> 7 #include<queue> 8 #include<set> 9 #include<map> 10 using namespace std; 11 #define eps 1e-6 12 #define For(i,a,b) for(int i=a;i<=b;i++) 13 #define Fore(i,a,b) for(int i=a;i>=b;i--) 14 #define lson l,mid,rt<<1 15 #define rson mid+1,r,rt<<1|1 16 #define mkp make_pair 17 #define pb push_back 18 #define sz size() 19 #define met(a,b) memset(a,b,sizeof(a)) 20 #define iossy ios::sync_with_stdio(false);cin.tie(0);cout.tie(0) //提高cin、cout的效率 21 #define fr freopen 22 #define pi acos(-1.0) 23 #define Vector Point 24 typedef pair<int,int> pii; 25 const long long linf=1LL<<62; 26 const int iinf=1<<30; 27 const double dinf=1e17; 28 const int Mod=1e9+9; 29 typedef long long ll; 30 typedef long double ld; 31 const int maxn=1000005; 32 int n; 33 struct Point{ 34 ll x,y; 35 int id; 36 Point(ll x=0,ll y=0):x(x),y(y) {} 37 Point operator - (const Point &a)const { return Point(x-a.x,y-a.y);} 38 bool operator == (const Point &a)const { return x==a.x && y==a.y; } 39 }; 40 41 ll gcd(ll a,ll b){ 42 while(b>0){ 43 ll t=b;b=a%b;a=t; 44 } 45 return a; 46 } 47 48 ll Cross(Vector a,Vector b){ //向量叉乘 49 return a.x*b.y-a.y*b.x; 50 } 51 ll Dot(Vector a,Vector b) { //向量相乘 52 return a.x*b.x+a.y*b.y; 53 } 54 bool onsg(Point p,Point a1,Point a2){ //判断点p是否在a1,a2组成的线段上 55 return Cross(a1-p,a2-p)==0 && Dot(a1-p,a2-p)<0; 56 //共线且反向 57 } 58 void ck(ll &c){ 59 if(c>0) c=1; 60 else if(c<0) c=-1; 61 } 62 int Ins(Point a1,Point a2,Point b1,Point b2){ //判断两个线段是否有交点 63 if(a1==b1 || a1==b2 || a2==b1 || a2==b2) return 1; //如果有端点相等,那么线段必然有交点 64 if(onsg(a1,b1,b2) || onsg(a2,b1,b2) || onsg(b1,a1,a2) || onsg(b2,a1,a2)) return 1; //如果有端点在另一条线段上,那么这两条线段必然有交点 65 66 ll c1=Cross(a2-a1,b1-a1),c2=Cross(a2-a1,b2-a1); //用c1*c2<0来判断b1,b2是否在a1~a2线段的两边 67 68 ll c3=Cross(b2-b1,a1-b1),c4=Cross(b2-b1,a2-b1); //用c3*c4<0来判断a1,a2是否在b1~b2线段的两边 69 ck(c1);ck(c2);ck(c3);ck(c4); 70 return c1*c2<0 && c3*c4<0; 71 } 72 73 set<pair<ll,ll> >c; 74 void chk(Point p,Vector v,Point q,Vector w){ //找到两条线段的交点坐标 75 Vector u=p-q; 76 ll v1=Cross(w,u),v2=Cross(v,w); 77 if(abs(v1*v.x)%v2!=0 || abs(v1*v.y)%v2!=0) return ; 78 ll xx,yy; 79 xx=p.x+v.x*v1/v2;yy=p.y+v.y*v1/v2; 80 c.insert(mkp(xx,yy)); 81 } 82 83 struct segm{ 84 Point p1,p2; 85 }; 86 87 segm ss[maxn]; 88 Point p1,p2; 89 void solve(){ 90 iossy; 91 cin>>n; 92 int ans=0; 93 For(i,1,n){ 94 cin>>p1.x>>p1.y>>p2.x>>p2.y; 95 ss[i].p1=p1;ss[i].p2=p2; 96 ans+=gcd(abs(ss[i].p2.x-ss[i].p1.x),abs(ss[i].p2.y-ss[i].p1.y))+1; //此处也可以直接用 __gcd()函数 97 }//利用gcd找出所有线段所能覆盖的整数点的总数 98 99 For(i,1,n){ 100 c.clear(); //每次清空set 101 For(j,i+1,n){ 102 int ct=Ins(ss[i].p1,ss[i].p2,ss[j].p1,ss[j].p2); 103 if(ct) chk(ss[i].p1,ss[i].p2-ss[i].p1,ss[j].p1,ss[j].p2-ss[j].p1); 104 //如果线段i与线段j有交点的话,将线段i与线段i+1~n的所有不重复的整数交点个数找出来 105 } 106 ans-=c.sz; 107 } 108 cout<<ans<<endl; 109 } 110 111 int main(){ 112 solve(); 113 return 0; 114 }
2018-09-09