【BZOJ】1132: [POI2008]Tro

时间:2023-01-20 20:22:59

题意

给\(n(1 \le n \le 3000)\)个点,求所有三角形的面积和。

分析

首先枚举一个点,发现把其它点按照关于这个点的极角排序后第\(i\)个点关于前面\(1\)到\(i-1\)的点组成的三角形的面积之和可以用前缀和和单调性来求出(因为有正负面积之分,而正负具有单调性)。

题解

所以我们维护枚举第一个点然后将其它点按照关于这个点为原点的极角排序。然后从左往右扫,计算第\(i\)个点和前\(i-1\)个点的正向面积之和和负向面积之和(叉积来求)。

极角排序的\(O(n^2logn)\)常数超大(doge).

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int getint() {
int x=0, c=getchar();
for(; c<48||c>57; c=getchar());
for(; c>47&&c<58; x=x*10+c-48, c=getchar());
return x;
}
const int N=3005;
struct ip {
int x, y;
double ang;
void init(int _x, int _y) {
x=_x;
y=_y;
ang=atan2(y, x);
}
void read() {
x=getint();
y=getint();
}
}a[N], p[N];
bool cmp(const ip &a, const ip &b) {
return a.ang<b.ang;
}
int sumx[N], sumy[N];
int main() {
int n=getint();
for(int i=1; i<=n; ++i) {
p[i].read();
}
ll ans=0;
for(int i=1; i<=n; ++i) {
int tot=0;
for(int j=1; j<=n; ++j) {
if(p[i].x!=p[j].x || p[i].y!=p[j].y) {
a[++tot].init(p[j].x-p[i].x, p[j].y-p[i].y);
}
}
sort(a+1, a+1+tot, cmp);
int pos=1;
for(int j=1; j<=tot; ++j) {
for(; pos<j && a[j].x*a[pos].y>a[j].y*a[pos].x; ++pos);
ans+=(ll)a[j].y*(sumx[j-1]-sumx[pos-1])-(ll)a[j].x*(sumy[j-1]-sumy[pos-1]);
ans+=(ll)a[j].x*sumy[pos-1]-(ll)a[j].y*sumx[pos-1];
sumx[j]=sumx[j-1]+a[j].x;
sumy[j]=sumy[j-1]+a[j].y;
}
}
ans/=3;
ans*=10;
ans/=2;
printf("%lld.%lld\n", ans/10, ans%10);
return 0;
}