problem
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;
}
统计三角形内点的个数。
考虑选五个点多算的是四边形包含一个点,容斥一下。
统计三角形内点的个数直接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;
}
固定最左下角的点和第三、第四个点,扫描线求出答案。具体看题解。
#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;
}