【题解】 CF437E The Child and Polygon 计算几何+dp

Legend

Link ( extrm{to Codeforces})

给定一个 (n) 个点的简单多边形,求三角剖分数目。

(3 le n le 200)

Editorial

做题做傻了,一个显然的区间 ( m{dp}) 都没看出来。

显然设 (dp_{l,r}) 表示使用了编号 (l sim r) 的所有点进行三角剖分的方案数。

转移 (dp_{l,r} = sum_{l < k < r} dp_{l,k} imes dp{k,r})

答案就是 (dp_{1,n})

显然有如下情况的是不合法的状态,不能转移:

  • (l o r) 的连边会穿过多边形的任何部位。
  • (l o r) 的连边在多边形外部。

第一个判断就直接判断,第二个有一个神仙的判断方法。

这条边会把多边形按下标分成两个部分,如果这条边在外部并与多边形无交则一定会增加多边形的面积。

不过其实这两个有一个共同的判断方法:

在枚举 (k) 的时候看 (k) 是不是凹进去的就好了,凹进去就不合法。

Code

判断部分写得非常丑……看完题解才发现有这么简单的判断方法。

但是毕竟写都写完了,还是不改了。

#include <bits/stdc++.h>

using namespace std;

#define LL long long

const int MX = 200 + 23;
const LL MOD = 1e9 + 7;

struct VECTOR{
	LL x ,y;
	VECTOR(LL X = 0 ,LL Y = 0){x = X ,y = Y;}
	VECTOR operator +(const VECTOR &B)const{return VECTOR(x + B.x ,y + B.y);}
	VECTOR operator -(const VECTOR &B)const{return VECTOR(x - B.x ,y - B.y);}
	LL operator *(const VECTOR &B)const{return x * B.y - y * B.x;}
	bool operator ==(const VECTOR &B)const{return x == B.x && y == B.y;}
	void Read(){cin >> x >> y;}
}P[MX];

struct SEG{
	VECTOR st ,d;
	SEG(){st = d = VECTOR(0 ,0);}
	SEG(VECTOR __st ,VECTOR __ed){
		st = __st ,d = __ed - __st;
	};
	void turn(){
		st = st + d;
		d = VECTOR(0 ,0) - d;
	}
};

int sgn(LL x){
	if(!x) return 0;
	return x > 0 ? 1 : -1;
}

bool PingXing(VECTOR A ,VECTOR B){
	return A.x * B.y - A.y * B.x == 0;
}

bool online(SEG a ,VECTOR b){
	// 询问点 b 是否在 a 上(含边缘)
	bool xless = b.x < a.st.x && b.x < (a.st + a.d).x;
	bool xmore = b.x > a.st.x && b.x > (a.st + a.d).x;
	bool yless = b.y < a.st.y && b.y < (a.st + a.d).y;
	bool ymore = b.y > a.st.y && b.y > (a.st + a.d).y;
	return (xless + xmore + yless + ymore) == 0;
}

int ok(SEG a ,SEG b){
	// 0 绝对不行
	// 1 共点
	// 2 绝对可以
	LL test1 = sgn(a.d * (b.st - a.st)) * sgn(a.d * (b.st + b.d - a.st));
	LL test2 = sgn(b.d * (a.st - b.st)) * sgn(b.d * (a.st + a.d - b.st));
	if(test1 > 0 || test2 > 0){
		// 连交点都没有
		return 2;
	}
	if(PingXing(a.d ,b.d)){
		// 有交点且平行就是共线
		if(online(a ,b.st) || online(a ,b.st + b.d)
		|| online(b ,a.st) || online(b ,a.st + a.d)){
			// 二者有交
			if(sgn(a.d.x) != sgn(b.d.x) || sgn(a.d.y) != sgn(b.d.y)){
				b.turn();
			}
			if(a.st + a.d == b.st || b.st + b.d == a.st)
				return 1;
			return 0;
		}
		
		return 2;
	}
	return (test1 <= 0 && test2 <= 0);
}

LL fad;
int n;
int check(int l ,int r){

	if(l == 0 && r == 4){
		puts("WTf");
	}
	SEG Main(P[l] ,P[r]);

	LL S = fad ,s1 = 0 ,s2 = 0;
	for(int i = l ; i <= r ; ++i){
		if(i == r){
			s1 += P[r] * P[l]; 
		}
		else{
			s1 += P[i] * P[i + 1];
		}
	}
	for(int i = r ; ; i = (i + 1) % n){
		if(i == l){
			s2 += P[l] * P[r];
			break;
		}
		s2 += P[i] * P[(i + 1) % n];
	}
	if(abs(S) != abs(s1) + abs(s2)) return 0;

	
	for(int i = 0 ; i < n ; ++i){
		SEG t(P[i] ,P[(i + 1) % n]);
		int judge = ok(Main ,t);
		if(i == r && ((i + 1) % n == l)) continue;
		if(judge == 0) return 0;
		if(judge == 1){
			if(i == l || (i + 1) % n == l || i == r || (i + 1) % n == r){
				continue;
			}
			return 0;
		}
	}
	return 1;
}

LL dp[MX][MX];

void test(){
	int n; cin >> n;
	while(n--){
		int x ,y;
		cin >> x >> y;
		printf("(%d,%d)
" ,x ,y);
	}
	SEG a(VECTOR(1 ,0) ,VECTOR(-1 ,0)) ,b(VECTOR(0 ,0) ,VECTOR(1 ,0));
	cout << ok(a ,b) <<endl;
	return ;
}

int main(){
	// test();
	cin >> n;
	for(int i = 0 ; i < n ; ++i){
		P[i].Read();
	}
	for(int i = 0 ; i < n ; ++i){
		fad += P[i] * P[(i + 1) % n];
	}
	// fad 为正数说明是顺时针给出
	for(int i = 1 ; i < n ; ++i){
		dp[i - 1][i] = 1;
	}
	for(int len = 2 ; len <= n ; ++len){
		for(int st = 0 ; st + len < n ; ++st){
			int ed = st + len;
			if(!check(st ,ed)){
				printf("[%d ,%d] is bad!
" ,st ,ed);

				continue;
			}
			for(int k = st + 1 ; k < ed ; ++k){
				dp[st][ed] = (dp[st][ed] + dp[st][k] * dp[k][ed]) % MOD;
			}
			printf("dp[%d][%d] = %lld
" ,st ,ed ,dp[st][ed]);
		}
	}
	cout << dp[0][n - 1] << endl;
	return 0;
}
原文地址:https://www.cnblogs.com/imakf/p/13828537.html