HZNU2019校赛-Little Sub and Triangles(小撒布与三角形)(二分答案)

Description

Little Sub loves triangles. Now he has a problem for you.

小撒布喜欢三角形。现在他有一个问题要给你。

Given n points on the two-dimensional plane, you have to answer many queries. Each query require you to calculate the number of triangles which are formed by three points in the given points set and their area S should satisfy l ≤ S ≤ r.

在二维平面上给定n个点,你要回答一些问题。每一个问题要求你去计算满足由三点围成的面积在l,r之内的三角形的个数。

Specially, to simplify the calculation, even if the formed triangle degenerate to a line or a point which S = 0, we still consider it as a legal triangle.

特别的,为了简化问题,就算这个"三角形”是一条直线或者一个点,使得它面积为0,我们仍然认为这是合法三角形。

Input

The first line contains two integer n, q(1 ≤ n ≤ 250, 1 ≤ q ≤ 100000), indicating the total number of points.

第一行包含两个整数n,q.表示点的总数

All points will be described in the following n lines by giving two integers x,y(−10^7 ≤ x,y ≤ 10^7) as their coordinates.

所有点都由下面n行输入所述,两个整数x,y,作为他们的坐标。

All queries will be described in the following q lines by giving two integers l, r(0 ≤ l ≤ r ≤ 10^18).

所有询问都由下列q行通过给定两个数l,r的方式给出。

 

Output

Output the answer in one line for each query.

每一次询问输出一个答案。

Samples

input:

4 2 0 1 100 100 0 0 1 0 0 50 0 2

output:

3 1

首先这个题给出了一堆坐标让我们计算出每个三角形的面积。组合方式有非常非常多,幸运的是不必考虑三角形边的关系,而且数据范围不大,那么可以直接用三个for循环枚举。

但是这里就有一个问题了:一般公式都不大好用,尚且还要计算边长,太麻烦。有什么公式可以用来解决给定点的坐标求三角形的面积的问题吗?

下面给出三角形面积公式的坐标行列式形式:

其中(a,b)(c,d)(e,f)为三角形三点坐标。坐标不必一一对应,若有负数取绝对值即可。

解决了面积,那么怎么去回答这些询问呢?

在上面计算出的三角形面积基本都是乱序的,对于乱序数据的查询没有快速的方法,但是对有序数据的查询就有快速办法了:二分查找。所以我们直接对面积数据组进行排序。

由于三角形面积数据很可能太多而且二分查找一次只能找一个,如果一个一个找并不现实。然而边界却只有两个:所以我们让边界去自行移动到面积数据组的相应的位置,这样两次二分即可搞定。

所以有如下思路

  1. 各点枚举,用行列式求得三角形面积(fabs相对于if判定负数再取相反数有一点耗时)
  2. 排序
  3. 把左右边界用二分移动到相应位置,最后的差值+1就是答案(也需要注意如果数据完全在区间之外的特判)

代码如下:

#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <algorithm>
#include <iomanip>
#include <cstring>
#include <cmath>
#define DETERMINATION main
#define lldin(a) scanf("%lld",&a)
#define din(a) scanf("%d",&a)
#define printlnlld(a) printf("%lld\n",a)
#define printlnd(a) printf("%d\n",a)
#define printlld(a) printf("%lld",a)
#define printd(a) printf("%d",a)
#define reset(a,b) memset(a,b,sizeof(a))
const long long int INF=0x3f3f3f3f;
using namespace std;
const double PI=acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod=1000000007;
const int tool_const=1999112620000907;
const int tool_const2=33;
inline ll Unnamed_Scanner()
{
    ll tmp=0,si=1;
    char c=getchar();
    while(c>'9'||c<'0')
    {
        if(c=='-')
            si=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        tmp=tmp*10+c-'0';
        c=getchar();
    }
    return si*tmp;
}
///Schlacht von Stalingrad
/**Although there will be many obstructs ahead,
the desire for victory still fills you with determination..**/
double squares[5646562];
struct point
{
    double x,y;
} points[750];
int DETERMINATION()
{
    ll n,q;
    n=Unnamed_Scanner(),q=Unnamed_Scanner();
    for(int i=1; i<=n; i++)
    {
        scanf("%lf %lf",&points[i].x,&points[i].y);
    }
    ll totality=1;
    for(int i=1; i<=n; i++)
        for(int j=i+1; j<=n; j++)
            for(int k=j+1; k<=n; k++)//行列式模拟
            {
                squares[totality]=fabs((points[i].x*(points[j].y-points[k].y)-
                                         points[i].y*(points[j].x-points[k].x)+points[j].x*points[k].y-
                                         points[j].y*points[k].x))/2;
                totality++;
                //cout<<squares[totality-1]<<endl;
            }
    totality--;
    sort(squares+1,squares+1+totality);
    for(int p=1; p<=q; p++)
    {
        ll llim=Unnamed_Scanner();
        ll rlim=Unnamed_Scanner();
        if(squares[1]>rlim||squares[totality]<llim)//特判
        {
            printf("0\n");
            continue;
        }
        ll ans1=INF,ans2=-1;
        ll left=1,right=totality;
        while(left<=right)//二分左边界
        {
            ll mid=(left+right)>>1;
            if(squares[mid]>=llim)
            {
                right=mid-1;
                ans1=min(ans1,mid);
            }
            else
                left=mid+1;
        }
        while(squares[ans1-1]==squares[ans1]&&ans1>1)//如果还有相等的元素则继续移动
            ans1--;
        left=1,right=totality;
        while(left<=right)
        {
            ll mid=(left+right)>>1;
            if(squares[mid]<=rlim)
            {
                left=mid+1;
                ans2=max(ans2,mid);
            }
            else
                right=mid-1;
        }

        while(squares[ans2+1]==squares[ans2]&&ans2<totality)
            ans2++;//cout<<ans1<<" "<<ans2<<endl;
        printlnlld(ans2-ans1+1);
    }
    return 0;
}
/*
3 5
0 0
10 0
0 10
*/

猜你喜欢

转载自blog.csdn.net/weixin_43874261/article/details/90018450