[CF1146H]Satanic Panic(dp)

题面

http://codeforces.com/contest/1146/problem/H

题解

由于五角星难以统计,并且一个五角星唯一对应它的外接五边形,考虑统计原图中凸五边形的数量。

设dp(i,j,k)表示从i号点开始,走到j号点结束,一共经过k个点,且经过的所有点顺次连接形成的路径为向量i->j右侧的凸包的方案数。

初始时,所有的dp(i,i,1)为1,其他为0。将图中所有的有向线段按极角升序排序,然后顺次枚举;设当前枚举到的有向线段是由第u号点连向第v号点的,那么枚举i,j,并进行转移dp(i,v,j+1) += dp(i,u,j)。极角排序的好处是不会破坏凸性,此式得以成立。

最终对所有dp(i,i,6)求和即可。

时间复杂度(O(n^3))

代码

#include<bits/stdc++.h>

using namespace std;

#define ll long long
#define rg register
#define In inline

const ll N = 300;

typedef pair<ll,ll>pll;

In ll read(){
	ll s = 0,ww = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-')ww = -1;ch = getchar();}
	while('0' <= ch && ch <= '9'){s = 10 * s + ch - '0';ch = getchar();}
	return s * ww;
}

struct vec{
	ll x,y;
	vec(){}
	vec(ll _x,ll _y){x = _x,y = _y;}
	In friend vec operator - (vec a,vec b){
		return vec(a.x - b.x,a.y - b.y);
	}
	In friend ll Cross(vec a,vec b){
		return a.x * b.y - a.y * b.x;
	}
	In friend bool InUpper(vec a){
		return a.y > 0 || (a.y == 0 && a.x > 0);
	}
}p[N+5];

In bool cmp(pll i,pll j){ //极角排序
	vec a = p[i.second] - p[i.first],b = p[j.second] - p[j.first];
	bool k1 = InUpper(a),k2 = InUpper(b);
	if(k1 != k2)return k1 < k2;
	return Cross(a,b) > 0;
}

ll f[N+5][N+5][7];
ll n;
pll seg[N*N+5];

int main(){
	n = read();
	for(rg int i = 1;i <= n;i++){
		ll x = read(),y = read();
		p[i] = vec(x,y);
	}	
	int cnt = 0;
	for(rg int i = 1;i <= n;i++)
		for(rg int j = 1;j <= n;j++)if(i != j)seg[++cnt] = make_pair(i,j);
	sort(seg + 1,seg + cnt + 1,cmp);
	for(rg int i = 1;i <= n;i++)f[i][i][1] = 1;
	for(rg int k = 1;k <= cnt;k++){
		int u = seg[k].first,v = seg[k].second;
		for(rg int i = 1;i <= n;i++)
			for(rg int j = 1;j <= 5;j++)f[i][v][j+1] += f[i][u][j];
	}	
	ll ans = 0;
	for(rg int i = 1;i <= n;i++)ans += f[i][i][6];
	cout << ans << endl;
	return 0;
}
原文地址:https://www.cnblogs.com/xh092113/p/12365060.html