HDU 5784 How Many Triangles

给你一堆点,找锐角三角形。

TWO POINTER 思想。

统计出所有锐角和直=钝角的数目。

做法是这样的:对每个点对所有点极角排序,然后TWO POINTER计算每一个锐角(一个边上有好几个点也会被统计好几次),直角钝角。然后ans=(锐角个数-直角钝角个数*2)/3;因为每一个角度可能也只可能出现在一个三角形中。

但是一上来没弄懂,所以队友想了一个思路也能过,先统计直角和钝角的个数。然后C(n,3)统计所有情况,不能构成三角形的是三点共线的情况,所以n^2logn统计三点共线(TWO POINTER)。然后再减去直角钝角个数即可。两种方法均可过。

下面是我和队友死磕的方法。

#include <cstdio>
#include <cstring>
#include <vector>
#include <cmath>
#include <stack>
#include <cstdlib>
#include <queue>
#include <map>
#include <iostream>
#include <algorithm>
#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
struct Point
{
    int x,y;
    Point(int _x = 0,int _y = 0)
    {
        x=_x;
        y=_y;
    }
    Point operator -(const Point &a) const
    {
        return Point(x-a.x,y-a.y);
    }
} p[2010];
int n;
LL xmul(Point a,Point b)
{
    return (a.x*1ll*b.y-a.y*1ll*b.x);
}
LL dot(Point a,Point b)
{
    return a.x*1ll*b.x+a.y*1ll*b.y;
}
bool cmp(Point a,Point b)
{
    if (a.y*1ll*b.y<=0)
    {
        if (a.y>0||b.y>0) return a.y<b.y;
        if (a.y==0&&b.y==0)return a.x<b.x;
    }
    return xmul(a,b)>0;
}
vector<Point>vec;
int main()
{
    while (scanf ("%d",&n)!=EOF)
    {
        LL zhidun=0,rui=0,ans=0;
        for (int i=0; i<n; ++i)
        {
            scanf ("%d%d",&p[i].x,&p[i].y);
        }
        double temp=0;
        for (int cor=0; cor<n; ++cor)
        {
            vec.clear();
            for (int i=0; i<n; ++i)
            {
                if (i!=cor)
                    vec.push_back(p[i]-p[cor]);
            }
            sort(vec.begin(),vec.end(),cmp);

            for (int i=0; i<n-1; ++i)
                vec.push_back(vec[i]);

            int same=0,acute=0,eles=0;
            for (int i=0; i<n-1; ++i)
            {
                while (same<n-1&&xmul(vec[i],vec[same])==0&&dot(vec[i],vec[same])>0) ++same;
                acute=max(acute,same);
                while (acute<i+n-1&&xmul(vec[i],vec[acute])>0&&dot(vec[i],vec[acute])>0) ++acute;
                eles=max(eles,acute);
                while (eles<i+n-1&&xmul(vec[i],vec[eles])>0) ++eles;
//                rui+=acute-same;
                zhidun+=eles-acute;
            }
//            下面计算以这个为核心,一条射线上三点共线的个数
//            printf ("cor(%d,%d)\n",p[cor].x,p[cor].y);
            for (int j=0,k=0, i=0; i<n-1; i=j)//一条直线只遍历一次
            {
                while (j<n-1&&xmul(vec[i],vec[j])==0&&dot(vec[i],vec[j])>0) ++j;
                int x=j-i;
                if (i==0&&j==n-1)//一下到头,不用继续扫,直接跳出
                {
                    temp+=1.0*(x*(x-1))/2;
                    break;
                }
                k=max(k,j);
                while (k<i+n-1&&xmul(vec[i],vec[k])>0) ++k;//第一次到达180位置
                int fir=k;

                while (k<i+n-1&&xmul(vec[i],vec[k])==0) ++k;//刚刚超过180位置
                int y=k-fir;
                x+=y;
                if (y==0)//反方向没有
                {
//                    printf ("2-----%f\n",1.0*(x*(x-1))/2);
                    temp+=0.1*(x*(x-1))/2;
                }
                else//两端都有,后面的必然会被再统计到,这次就计算一半
                {
//                    printf ("4-----%f\n",1.0*(x*(x-1))/4);
                    temp+=1.0*(x*(x-1))/4;
                }
            }
//            cout<<"temp="<<temp<<endl;
        }
        LL tt=(LL)(temp+0.5);//temp有精度问题
        ans=n*(n-1)*1ll*(n-2)/6-tt/3-zhidun;//所有三个点减去三点共线情况就是三角形的情况,再减去直角钝角的就是答案
        printf ("%I64d\n",ans);
    }
    return 0;
}

下面是标解的方法:

#include <cstdio>
#include <cstring>
#include <vector>
#include <cmath>
#include <stack>
#include <cstdlib>
#include <queue>
#include <map>
#include <iostream>
#include <algorithm>


using namespace std;
typedef long long LL;
struct Point
{
    int x,y;
    Point(int _x = 0,int _y = 0)
    {
        x=_x;
        y=_y;
    }
    Point operator -(const Point &a) const
    {
        return Point(x-a.x,y-a.y);
    }
}p[2010];
int n;

LL xmul(const Point &a,const Point &b)
{
    return a.x * 1ll * b.y - a.y * 1ll * b.x;
}

LL dot(const Point &a,const Point &b)
{
    return a.x * 1ll * b.x + a.y * 1ll * b.y;
}

bool cmp(const Point &a,const Point &b)
{
    if (a.y * 1ll * b.y <= 0)
    {
        if (a.y > 0 || b.y > 0) return a.y < b.y;
        if (a.y == 0 && b.y == 0) return a.x < b.x;
    }
    return xmul(a,b) > 0;
}

vector<Point>vec;
int main()
{
    while (scanf ("%d",&n)!=EOF)
    {
        LL zhidun=0,rui=0,ans=0;
        for (int i=0;i<n;++i)
        {
            scanf ("%d%d",&p[i].x,&p[i].y);
        }
        for (int cor=0;cor<n;++cor)
        {
            vec.clear();
            for (int i=0;i<n;++i)
            {
                if (i!=cor)
                vec.emplace_back(p[i]-p[cor]);
            }
            sort(vec.begin(),vec.end(),cmp);
            vec.insert(vec.end(),vec.begin(),vec.end());

            int j=0,k=0,r=0;
            for (int i=0;i<n-1;++i)
            {
                while (j<i+n-1&&xmul(vec[i],vec[j])==0&&dot(vec[i],vec[j])>0) ++j;
                k=max(k,j);
                while (k<i+n-1&&xmul(vec[i],vec[k])>0&&dot(vec[i],vec[k])>0) ++k;
                r=max(r,k);
                while (r<i+n-1&&xmul(vec[i],vec[r])>0) ++r;
                rui+=k-j;
                zhidun+=r-k;
            }
        }
        ans=(rui-2*zhidun)/3;
        printf ("%I64d\n",ans);
    }
    return 0;
}

于是又多了一个n^2logn判断三点共线个数的版子,首发,在网上还没看到

#include <cstdio>
#include <cstring>
#include <vector>
#include <cmath>
#include <stack>
#include <cstdlib>
#include <queue>
#include <map>
#include <iostream>
#include <algorithm>
#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
struct Point
{
    int x,y;
    Point(int _x = 0,int _y = 0)
    {
        x=_x;
        y=_y;
    }
    Point operator -(const Point &a) const
    {
        return Point(x-a.x,y-a.y);
    }
} p[2010];
int n;
LL xmul(Point a,Point b)
{
    return (a.x*1ll*b.y-a.y*1ll*b.x);
}
LL dot(Point a,Point b)
{
    return a.x*1ll*b.x+a.y*1ll*b.y;
}
bool cmp(Point a,Point b)
{
    if (a.y*1ll*b.y<=0)
    {
        if (a.y>0||b.y>0) return a.y<b.y;
        if (a.y==0&&b.y==0)return a.x<b.x;
    }
    return xmul(a,b)>0;
}
vector<Point>vec;
int main()
{
    while (scanf ("%d",&n)!=EOF)
    {
        for (int i=0; i<n; ++i)
        {
            scanf ("%d%d",&p[i].x,&p[i].y);
        }
        double temp=0;
        for (int cor=0; cor<n; ++cor)
        {
            vec.clear();
            for (int i=0; i<n; ++i)
            {
                if (i!=cor)
                    vec.push_back(p[i]-p[cor]);
            }
            sort(vec.begin(),vec.end(),cmp);

            for (int i=0; i<n-1; ++i)
                vec.push_back(vec[i]);

//            下面计算以这个为核心,一条射线上三点共线的个数
//            printf ("cor(%d,%d)\n",p[cor].x,p[cor].y);
            for (int j=0,k=0, i=0; i<n-1; i=j)//一条直线只遍历一次
            {
                while (j<n-1&&xmul(vec[i],vec[j])==0&&dot(vec[i],vec[j])>0) ++j;
                int x=j-i;
                if (i==0&&j==n-1)//一下到头,不用继续扫,直接跳出
                {
                    temp+=1.0*(x*(x-1))/2;
                    break;
                }
                k=max(k,j);
                while (k<i+n-1&&xmul(vec[i],vec[k])>0) ++k;//第一次到达180位置
                int fir=k;

                while (k<i+n-1&&xmul(vec[i],vec[k])==0) ++k;//刚刚超过180位置
                int y=k-fir;
                x+=y;
                if (y==0)//反方向没有
                {
//                    printf ("2-----%f\n",1.0*(x*(x-1))/2);
                    temp+=0.1*(x*(x-1))/2;
                }
                else//两端都有,后面的必然会被再统计到,这次就计算一半
                {
//                    printf ("4-----%f\n",1.0*(x*(x-1))/4);
                    temp+=1.0*(x*(x-1))/4;
                }
            }
//            cout<<"temp="<<temp<<endl;
        }
        LL tt=(LL)(temp+0.5);//temp有精度问题

        printf ("%I64d\n",tt/3);
    }
    return 0;
}

 

posted on 2016-08-03 11:51  very_czy  阅读(380)  评论(0编辑  收藏  举报

导航