題意
給出 條線段,求平面中一共有多少個閉合區域。保證不存在三條線段相交與一點,並且線段都是規範相交,即不存在相交點在線段端點。
思路
此題是一個公式題,因此直接上公式。
基本術語
- 平面圖:不存在邊交叉的二維平面上的圖
- 連通平面圖:平面圖上的圖形相互連通,即可以僅依靠圖中的邊從任意一個節點到達另外一個節點
歐拉平面圖公式
- 基本公式(針對連通平面圖)
- ,其中 爲點數, 爲邊數, 爲面數(即閉合區域)
- 此處的 包括最外層的面
- 此公式可用數學歸納法證明,證明過程簡單易懂,讀者可自行查閱
- 廣義公式(針對平面圖)
- ,其中 爲平面圖中的連通塊個數
- 此公式可根據基本公式推出,推導過程較爲簡單,讀者可自行思考
本題做法
由於本題僅給出了線段,並不滿足平面圖的限制,因此我們可以將線段的交點當作一個新的點,將原來的圖形轉化成平面圖,轉化過程如下圖所示。(題解 )
因此在轉化後的圖中,點數變爲 ,邊數變爲 ,其中 爲直線交點個數, 爲連通塊個數,即最終答案如下:(本題忽略最外層的點)
代碼
#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;
}