HDU 3685 Rotational Painting(多边形质心+凸包)(2010 Asia Hangzhou Regional Contest)

时间:2022-07-26 12:02:30
Problem Description
Josh Lyman is a gifted painter. One of his great works is a glass painting. He creates some well-designed lines on one side of a thick and polygonal glass, and renders it by some special dyes. The most fantastic thing is that it can generate different meaningful paintings by rotating the glass. This method of design is called “Rotational Painting (RP)” which is created by Josh himself.

You are a fan of Josh and you bought this glass at the astronomical sum of money. Since the glass is thick enough to put erectly on the table, you want to know in total how many ways you can put it so that you can enjoy as many as possible different paintings hiding on the glass. We assume that material of the glass is uniformly distributed. If you can put it erectly and stably in any ways on the table, you can enjoy it.

More specifically, if the polygonal glass is like the polygon in Figure 1, you have just two ways to put it on the table, since all the other ways are not stable. However, the glass like the polygon in Figure 2 has three ways to be appreciated. 
HDU 3685 Rotational Painting(多边形质心+凸包)(2010 Asia Hangzhou Regional Contest)
Pay attention to the cases in Figure 3. We consider that those glasses are not stable.
HDU 3685 Rotational Painting(多边形质心+凸包)(2010 Asia Hangzhou Regional Contest)

 
Input
The input file contains several test cases. The first line of the file contains an integer T representing the number of test cases. 
For each test case, the first line is an integer n representing the number of lines of the polygon. (3<=n<=50000). Then n lines follow. The ith line contains two real number xi and yi representing a point of the polygon. (xi, yi) to (xi+1, yi+1) represents a edge of the polygon (1<=i<n), and (xn,yn) to (x1, y1) also represents a edge of the polygon. The input data insures that the polygon is not self-crossed.
 
Output
For each test case, output a single integer number in a line representing the number of ways to put the polygonal glass stably on the table.
 
题目大意:给一个简单多边形,问有多少种方法可以把这个多边形竖直稳定地放在一个平面上,如图所示。
思路:物理学告诉我们,要解决这个问题,首先要求出这个多边形质心,然后枚举每一种放法,看质心到地板的垂线是否在底边之间。
令G(i) = cross(p[i], p[i+1]), 质心为t,多边形的点集为p,cross为叉积。
那么t.x = sum(cross(p[i], p[i+1]) / 2 * (p[i].x + p[i + 1].x) / 3) / sum(cross(p[i], p[i+1]) / 2)
t.y = sum(cross(p[i], p[i+1]) / 2 * (p[i].y + p[i + 1].y) / 3) / sum(cross(p[i], p[i+1]) / 2)
这里不证明。
然后在不考虑稳定的情况下,多边形的放法显然取决于这个多边形的凸包的边数。
枚举凸包的每一条边,判断质心到地板的垂线的垂足是否在凸包的那条边之中。
若质心为O,底边的两点分别为A、B,那么垂足在AB上当且仅当∠OAB和∠OBA都为锐角。
那么向量AO和向量AB的点积为正数,那么∠OAB为锐角,∠OBA同理。
此题解决。
 
代码(281MS):
 #include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
typedef long long LL; const int MAXN = ;
const double EPS = 1e-; inline int sgn(double x) {
return (x > EPS) - (x < -EPS);
} struct Point {
double x, y;
Point(double x = , double y = ): x(x), y(y) {}
void read() {
scanf("%lf%lf", &x, &y);
}
Point operator + (const Point &rhs) const {
return Point(x + rhs.x, y + rhs.y);
}
Point operator - (const Point &rhs) const {
return Point(x - rhs.x, y - rhs.y);
}
double operator * (const Point &rhs) const {
return x * rhs.x + y * rhs.y;
}
Point operator / (const double &rhs) const {
return Point(x / rhs, y / rhs);
}
bool operator < (const Point &rhs) const {
if(y != rhs.y) return y < rhs.y;
return x < rhs.x;
}
};
typedef Point Vector; double cross(const Point &a, const Point &b) {
return a.x * b.y - a.y * b.x;
} double cross(const Point &sp, const Point &op, const Point &ep) {
return cross(sp - op, ep - op);
} void Graham_scan(Point *p, int n, int *stk, int &top) {
sort(p, p + n);
top = ;
stk[] = ; stk[] = ;
for(int i = ; i < n; ++i) {
while(top && cross(p[stk[top - ]], p[stk[top]], p[i]) <= ) --top;
stk[++top] = i;
}
int len = top;
stk[++top] = n - ;
for(int i = n - ; i >= ; --i) {
while(top != len && cross(p[stk[top - ]], p[stk[top]], p[i]) <= ) --top;
stk[++top] = i;
}
} Point barycenter(Point *p, int n) {
double area = ;
Point res;
for(int i = ; i < n; ++i) {
double t = cross(p[i], p[i + ]) / ;
res.x += t * (p[i].x + p[i + ].x) / ;
res.y += t * (p[i].y + p[i + ].y) / ;
area += t;
}
return res / area;
} Point p[MAXN];
int stk[MAXN], top;
int n, T; int main() {
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
for(int i = ; i < n; ++i) p[i].read();
p[n] = p[];
Point O = barycenter(p, n);
Graham_scan(p, n, stk, top); int ans = ;
for(int i = ; i < top; ++i) {
Point &A = p[stk[i]], &B = p[stk[i + ]];
ans += (sgn((O - A) * (B - A)) > && sgn((O - B) * (A - B)) > );
}
printf("%d\n", ans);
}
}