【省选模拟】多边形(凸包DP)

4 篇文章 0 订阅

在这里插入图片描述

  • 钦定一个点为起点,令 f i , j f_{i,j} fi,j 表示凸包的最后两个点为 i , j i,j i,j 的方案数。
    枚举 k k k 转移到 f k , i f_{k,i} fk,i 需要满足 △ p i k \triangle pik pik 中没有点,按叉积预处理与原点形成的三角形包涵的点数后就可以 O ( n 4 ) O(n^4) O(n4) 做,发现这个可以前缀和优化,按与 i i i 形成的极角排序,那么对于 k k k 合法的转移是一个前缀,双指针扫一遍,于是就可以做到 O ( n 3 ) O(n^3) O(n3)
#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
cs int N = 505;
cs int Mod = 1e9 + 7;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
int dec(int a, int b){ return a - b < 0 ? a - b + Mod : a - b; }
void Add(int &a, int b){ a = add(a, b); }
void Mul(int &a, int b){ a = mul(a, b); }
struct Pnt{
	int x, y; Pnt(int _x=0, int _y=0){ x = _x; y = _y; }
	Pnt operator + (cs Pnt &a){ return Pnt(x+a.x,y+a.y); }
	Pnt operator - (cs Pnt &a){ return Pnt(x-a.x,y-a.y); }
	int operator * (cs Pnt &a){ return x*a.y-y*a.x; }
}p[N];
bool cmp(Pnt a, Pnt b){ return a.y==b.y?a.x<b.x:a.y<b.y; }
int n, ps[N][N<<1];
int dp[N][N], Cons;
int Cross(int a, int b, int c){ return (p[a]-p[c])*(p[b]-p[c]); }
bool comp(int a, int b){ return Cross(a,b,Cons) > 0; } 
int main(){
	n = read();
	for(int i=1; i<=n; i++) p[i].x=read(), p[i].y=read();
	sort(p+1, p+n+1, cmp);
	for(int i=1; i<=n; i++){
		for(int j=1; j<=n; j++) ps[i][j]=j;
		ps[i][i]=1; Cons=i;
		sort(ps[i]+2, ps[i]+i+1, comp);
		sort(ps[i]+i+1, ps[i]+n+1, comp);
		for(int j=n+1; j<n+n; j++) ps[i][j] = ps[i][j-n+1];
	} 
	int ans = 0;
	for(int i=1; i<=n; i++){
		memset(dp,0,sizeof(dp));
		for(int j=i+1; j<=n; j++){
			static int ql[N],qr[N]; int tl=0, tr=0, nx=ps[i][j], p=0;
			for(int k=2; k<=n; k++) if(ps[nx][k]==i){ p=k; break; }
			for(int k=p; k<p+n; k++){
				if(Cross(ps[nx][k],i,nx)>0) qr[++tr]=ps[nx][k];
				else ql[++tl]=ps[nx][k];
			} 
			int sm=0, l=1, r=1;
			while(r<=tr){
				if(l>tl||Cross(qr[r],ql[l],nx)<0) 
				Add(dp[nx][qr[r]],sm), ++r;
				else Add(sm,dp[ql[l]][nx]), ++l;
			}
			int pre=0;
			while(tr){
				if(!pre||Cross(qr[tr],pre,i)>0)
				Add(dp[nx][qr[tr]],1), pre=qr[tr];
				else dp[nx][qr[tr]]=0; tr--;
			}
			for(int k=j+1; k<=n; k++) Add(ans,dp[nx][ps[i][k]]);
		}
	} cout<<ans; return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值