【BAPC 2017】Hoarse Horses【歐拉平面圖公式】

題意

給出 nn 條線段,求平面中一共有多少個閉合區域。保證不存在三條線段相交與一點,並且線段都是規範相交,即不存在相交點在線段端點。(1n1000)(1\leq n\leq 1000)

題目鏈接:linklink


思路

此題是一個公式題,因此直接上公式。

基本術語

  • 平面圖:不存在邊交叉的二維平面上的圖
  • 連通平面圖:平面圖上的圖形相互連通,即可以僅依靠圖中的邊從任意一個節點到達另外一個節點

歐拉平面圖公式

  • 基本公式(針對連通平面圖)
    • VE+F=2V-E+F=2,其中 VV 爲點數,EE 爲邊數,FF 爲面數(即閉合區域)
    • 此處的 FF 包括最外層的面
    • 此公式可用數學歸納法證明,證明過程簡單易懂,讀者可自行查閱
  • 廣義公式(針對平面圖)
    • VE+F=1+CV-E+F=1+C,其中 CC 爲平面圖中的連通塊個數
    • 此公式可根據基本公式推出,推導過程較爲簡單,讀者可自行思考

本題做法

由於本題僅給出了線段,並不滿足平面圖的限制,因此我們可以將線段的交點當作一個新的點,將原來的圖形轉化成平面圖,轉化過程如下圖所示。(題解 linklink
在這裏插入圖片描述
因此在轉化後的圖中,點數變爲 (2n+inter)(2*n+inter),邊數變爲 (n+2inter)(n+2*inter),其中 interinter 爲直線交點個數,compcomp 爲連通塊個數,即最終答案如下:(本題忽略最外層的點)
F=C+EV=comp+(n+2inter)(2n+inter)=comp+intern F=C+E-V=comp+(n+2*inter)-(2*n+inter)=comp+inter-n


代碼

#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof a);
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define per(i,a,b) for(int i = a; i >= b; i--)
typedef long long ll;
const int N = 1e4+100;
using namespace std;

int n, inter, vis[N], comp;
vector<int> G[N];

inline int sgn(ll x) { return x >= 0 ? x > 0 : -1;}

struct Point{
	ll x,y;
	Point() {}
	Point(ll _x, ll _y) : x(_x), y(_y) {}
	Point operator-(Point p) { return {x-p.x, y-p.y}; }
	ll det(Point p) { return x*p.y-y*p.x; } //叉積
	ll dot(Point p) { return x*p.x+y*p.y; }	//點積
}P1[N],P2[N];

struct Line{
	Point s,e;
	Line() {}
	Line(Point _s, Point _e){
		s = _s;
		e = _e;
	}
	int segcrosseg(Line v){
		int d1 = sgn((e-s).det(v.s-s));
		int d2 = sgn((e-s).det(v.e-s));
		int d3 = sgn((v.e-v.s).det(s-v.s));
		int d4 = sgn((v.e-v.s).det(e-v.s));
		if(((d1^d2) == -2) && (d3^d4) == -2) return 2;
		return (d1==0 && sgn((v.s-s).dot(v.s-e)) <= 0) ||
			   (d2==0 && sgn((v.e-s).dot(v.e-e)) <= 0) ||
			   (d3==0 && sgn((s-v.s).dot(s-v.e)) <= 0) ||
			   (d4==0 && sgn((e-v.s).dot(e-v.e)) <= 0);
	}
};

void dfs(int x){
	if(vis[x]) return;
	vis[x] = 1;
	for(auto y:G[x]) dfs(y);
}

int main()
{
	scanf("%d",&n);
	rep(i,1,n) scanf("%lld%lld%lld%lld",&P1[i].x,&P1[i].y,&P2[i].x,&P2[i].y);
	rep(i,1,n){
		Line l1 = {P1[i], P2[i]};
		rep(j,i+1,n){
			Line l2 = {P1[j], P2[j]};
			if(l1.segcrosseg(l2) != 0){
				G[i].push_back(j);
				G[j].push_back(i);
				inter++;
			}
		}
	}
	rep(i,1,n)
		if(!vis[i]){
			comp++;
			dfs(i);
		}
	printf("%d\n", inter+comp-n);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章