[BZOJ1914][Usaco2010 OPen]Triangle Counting 数三角形

[Usaco2010 OPen]Triangle Counting 数三角形

Description
在一只大灰狼偷偷潜入Farmer Don的牛群被群牛发现后,贝西现在不得不履行着她站岗的职责。从她的守卫塔向下瞭望简直就是一件烦透了的事情。她决定做一些开发智力的小练习,防止她睡着了。想象牧场是一个X,Y平面的网格。她将N只奶牛标记为1…N (1 <= N <= 100,000),每只奶牛的坐标为X_i,Y_i (-100,000 <= X_i <= 100,000;-100,000 <= Y_i <= 100,000; 1 <= i <=N)。然后她脑海里想象着所有可能由奶牛构成的三角形。如果一个三角形完全包含了原点(0,0),那么她称这个三角形为“黄金三角形”。原点不会落在任何一对奶牛的连线上。另外,不会有奶牛在原点。给出奶牛的坐标,计算出有多少个“黄金三角形”。顺便解释一下样例,考虑五只牛,坐标分别为(-5,0), (0,2), (11,2), (-11,-6), (11,-5)。下图是由贝西视角所绘出的图示。
Input
第一行:一个整数: N 第2到第N+1行: 每行两个整数X_i,Y_i,表示每只牛的坐标
Output
* 第一行: 一行包括一个整数,表示“黄金三角形的数量”
Sample Input
5
-5 0
0 2
11 2
-11 -6
11 -5
Sample Output
5

Code
我们利用补集转化思想,考虑不包含原点的三角形个数.
任取一点 P ,原点与P的所在直线 l 将整个平面划分为两个半平面,而对于任意其他两点A,B,若 A,B,PAB ,这样每个不包含远点的三角形会算两次,因此我们可以只算一个方向的半平面.
基于此,我们对所有点按极角排序,利用 twopointers 扫描解决问题

Solution

#include <bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for (int i = (l); i <= (r); i++)
#define per(i, r, l) for (int i = (r); i >= (l); i--)
#define MS(_) memset(_, 0, sizeof(_))
#define MP make_pair
#define PB push_back
#define debug(...) fprintf(stderr, __VA_ARGS__)
template<typename T> inline void read(T &x){
    x = 0; T f = 1; char ch = getchar();
    while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
    while (isdigit(ch))  {x = x * 10 + ch - '0'; ch = getchar();}
    x *= f;
}

typedef long long ll;
const double eps = 1e-7;
const int N = 111111;

struct Vec{
    double x, y, angle;
    Vec() {}
    Vec(double _x, double _y) : x(_x), y(_y) {angle = atan2(y, x);}
    inline void cal_angle() {angle = atan2(y, x);}
};
inline Vec operator + (const Vec &a, const Vec &b) {return Vec(a.x+b.x, a.y+b.y);}
inline Vec operator - (const Vec &a, const Vec &b) {return Vec(a.x-b.x, a.y-b.y);}
template<typename T> inline Vec operator * (const Vec &a, T b) {return Vec(a.x*b, a.y*b);}
template<typename T> inline Vec operator * (T a, const Vec &b) {return Vec(a*b.x, a*b.y);}
inline Vec operator / (const Vec &a, double b) {return Vec(a.x/b, a.y/b);}
inline double dot(const Vec &a, const Vec &b){return a.x*b.x + a.y*b.y;}
inline double cross(const Vec &a, const Vec &b){return a.x*b.y - a.y*b.x;}

typedef Vec Poi;
inline bool angle_cmp(Poi a, Poi b) {return a.angle < b.angle;}
inline bool gt0(double x) {return x-eps>0;}

int n;
Poi a[N];
ll ans;

int main(){
    read(n);
    rep(i, 1, n) {read(a[i].x); read(a[i].y); a[i].cal_angle();}
    sort(a+1, a+1+n, angle_cmp);

    int last = 1, cnt = 0;
    rep(i, 1, n){
        while ((last%n+1 != i) && gt0(cross(a[i], a[last%n+1]))) last++, cnt++;
        ans += 1ll * cnt * (cnt-1) / 2;
        cnt--;
    }

    printf("%lld\n", 1ll*n*(n-1)*(n-2)/6 - ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值