【题解】codeforces Forethought Future Cup - Elimination Round - H

problem

solution1

DP
所求5角星等价于找5个点的凸包数量
把所有边按照极角排序,依次枚举边转移
这样就只需要记录第一个点和当前点,不需要记录最后一条边,凸包一定合法(因为没有三点共线的情况,否则要特殊处理共线情况)

#include
using namespace std;

typedef long long ll;
const int maxn = 320;

struct point{
	ll x,y;
	point(){}
	point(ll a,ll b):x(a),y(b){};
	point operator - (point a){
		return point(x - a.x,y - a.y);
	}
	bool operator < (point a)const{
		if ( x == a.x ) return y < a.y;
		return x < a.x;
	}
}dt[maxn];
struct line{
	double ang; int id1,id2;
	bool operator < (line a)const{
		return ang < a.ang;
	}
}V[maxn * maxn];
int n,tot;
ll f[maxn][maxn][6];

ll xmul(point a,point b){ return a.x * b.y - a.y * b.x; }
void init(){
	for (int i = 1 ; i <= n ; i++){
		for (int j = i + 1 ; j <= n ; j++){
			V[++tot] = (line){atan2(dt[j].y - dt[i].y,dt[j].x - dt[i].x),i,j};
			V[++tot] = (line){atan2(dt[i].y - dt[j].y,dt[i].x - dt[j].x),j,i};
		}
	}
	sort(V + 1,V + tot + 1);
}
int main(){
	scanf("%d",&n);
	for (int i = 1 ; i <= n ; i++) scanf("%lld %lld",&dt[i].x,&dt[i].y);
	init();
	for (int i = 1 ; i <= n ; i++) f[i][i][0] = 1;
	for (int i = 1 ; i <= tot ; i++){
		int x = V[i].id1 , y = V[i].id2; //x -> y
		for (int j = 1 ; j <= n ; j++){
			for (int k = 0 ; k <= 4 ; k++){
				f[j][y][k + 1] += f[j][x][k];
			}
		}
	}
	ll ans = 0;
	for (int i = 1 ; i <= n ; i++) ans += f[i][i][5];
		cout<<ans<<endl;
}

solution2

统计三角形内点的个数。
考虑选五个点多算的是四边形包含一个点,容斥一下。
统计三角形内点的个数直接bitset,统计一条边左侧的点,注意遍历边的顺序要逆时针

#include
using namespace std;

typedef long long ll;
const int maxn = 320;

struct point{
	ll x,y;
	point(){}
	point(ll a,ll b):x(a),y(b){};
	point operator - (point a){
		return point(x - a.x,y - a.y);
	}
	bool operator < (point a)const{
		if ( x == a.x ) return y < a.y;
		return x < a.x;
	}
}dt[maxn];

int n;
bitset <maxn> left_[maxn][maxn];

ll xmul(point a,point b){ return a.x * b.y - a.y * b.x; }
void init(){
	sort(dt + 1,dt + n + 1);
	for (int i = 1 ; i <= n ; i++){
		for (int j = 1 ; j <= n ; j++){
			for (int k = 1 ; k <= n ; k++){
				if ( xmul(dt[j] - dt[i],dt[k] - dt[i]) > 0 ) left_[i][j][k] = 1;
			}
		}
	}
	//for (int i = 1 ; i <= n ; i++) cout<
}
int main(){
	scanf("%d",&n);
	for (int i = 1 ; i <= n ; i++) scanf("%lld %lld",&dt[i].x,&dt[i].y);
	init();
	ll ans = (ll)n * (n - 1) * (n - 2) * (n - 3) * (n - 4) / 120 , sum = 0;
	for (int i = 1 ; i <= n ; i++){
		for (int j = i + 1 ; j <= n ; j++){
			for (int k = i + 1 ; k <= n ; k++){
				if ( !left_[i][k][j] ) continue;
				ll cnt = (left_[i][k] & left_[k][j] & left_[j][i]).count();
		//		cout<
				sum += cnt * (n - 4);
				ans += cnt * (cnt - 1) / 2;
			}
		}
	}
	//cout<
	//cout<
	ans -= sum / 2;
	cout<<ans<<endl;
}

solution3

固定最左下角的点和第三、第四个点,扫描线求出答案。具体看题解。

#include
using namespace std;

typedef long long ll;
const int maxn = 320;

struct point{
	ll x,y; int id;
	point(){}
	point(ll a,ll b):x(a),y(b){};
	point operator - (point a){
		return point(x - a.x,y - a.y);
	}
	/*bool operator < (point a)const{
		if ( y == a.y ) return x < a.x;
		return y < a.y;
	}*/
}dt[maxn],base,U[maxn],cur[maxn],D[maxn];

int n,tot;
ll ans,cntD[maxn][maxn],cntU[maxn][maxn];

ll xmul(point a,point b){ return a.x * b.y - a.y * b.x; }
bool cmp(point x,point y){
	return xmul(x - base,y - base) > 0;
}
void solve2(point A,point B){
	int up_cnt = 0 , down_cnt = 0;
	for (int i = 1 ; i <= tot ; i++){
		ll d = xmul(B - A,cur[i] - A);
		if ( d > 0 ) U[++up_cnt] = cur[i];
		else if ( d < 0 ) D[++down_cnt] = cur[i]; 
	}
	base = B;
	sort(U + 1,U + up_cnt + 1,cmp);
	sort(D + 1,D + down_cnt + 1,cmp);
	int num = 0;
	for (int i = 1 ; i <= up_cnt; i++){
		while ( num < down_cnt && xmul(D[num + 1] - B,B - U[i]) > 0 ) num++;
		cntD[B.id][U[i].id] = num;
	}
}
void solve3(point A,point B){
	int up_cnt = 0 , down_cnt = 0;
	for (int i = 1 ; i <= tot ; i++){
		ll d = xmul(B - A,cur[i] - A);
		if ( d > 0 ) U[++up_cnt] = cur[i];
		else if ( d < 0 ) D[++down_cnt] = cur[i]; 
	}
	base = B;
	sort(U + 1,U + up_cnt + 1,cmp);
	sort(D + 1,D + down_cnt + 1,cmp);
	int num = 1;
	for (int i = 1 ; i <= down_cnt; i++){
		while ( num <= up_cnt && xmul(B - D[i],U[num] - B) <= 0 ) num++;
		cntU[D[i].id][B.id] = up_cnt - num + 1;
	}
}
void solve(int id){ // dt[id] is the bottom and left most point
	cur[tot = 1] = dt[id];
	for (int i = 1 ; i <= n ; i++){
		if ( id != i && (dt[id].y < dt[i].y || (dt[id].y == dt[i].y && dt[id].x < dt[i].x)) ) cur[++tot] = dt[i];
	}
	for (int i = 2 ; i <= tot ; i++){
		solve2(cur[1],cur[i]);
		solve3(cur[1],cur[i]);
	}
	for (int i = 1 ; i <= n ; i++){
		for (int j = 1 ; j <= n ; j++){
			ans += cntU[i][j] * cntD[i][j];
			cntU[i][j] = cntD[i][j] = 0;
		}
	}
}
int main(){
	scanf("%d",&n);
	for (int i = 1 ; i <= n ; i++) scanf("%lld %lld",&dt[i].x,&dt[i].y) , dt[i].id = i;
	for (int i = 1 ; i <= n ; i++){
		solve(i);
	}
	cout<<ans<<endl;
}

你可能感兴趣的:(计算几何,codeforces)