题意: 给出1000个点(无3点共线), 有1000000个询问, 三个点组成的三角形内有多少个点
题目连接: http://acm.fzu.edu.cn/problem.php?pid=1973
因为询问个数太多直接暴力的话会超时的:
所以需要预处理: 枚举算出两两点组成的线段正下方有多少个点,记为under[i][j];暴力枚举的话也是 O(n^3),也会超时,所以利用极角排序加树状数组优化
三角形i, j, k内的点个数为 abs( under[i][j] + under[j][k] + under[k][i] ) ;
这个公式可以类比为求三角形面积的叉乘法
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define M 1010
struct Point
{
int x, y, id;
int pos;
}p[M], tmp[M], v;
int n;
bool cmp( Point a, Point b )
{
return a.x < b.x || ( a.x == b.x && a.y < b.y );
}
bool cmpp( Point a, Point b )
{
//long long
long long a1 = (b.y-v.y), a2 = (a.x-v.x), a3 = (a.y-v.y), a4 = (b.x-v.x);
return a1*a2 > a3*a4;
}
bool cmpid( Point a, Point b )
{
return a.id < b.id;
}
int sum[M], under[M][M]; //under[i][j]存的是线段i,j正下方有几个点,其中左端点正下方的点不计算,右端点正下方的点计算;
//under[i][j] = under[j][i];
void add( int i )
{
while( i < M ){
sum[i]++;
i += i&-i;
}
}
int query( int i )
{
int ans = 0;
while( i > 0 ){
ans += sum[i];
i -= i&-i;
}
return ans;
}
void init()
{
int m, a, b;
sort( p, p+n, cmp ); //按 x, y 轴排序
for( int i = 0; i < n; i++ ){
v = p[i]; //枚举以p[i]为左端点的线段
m = 0;
for( int j = i+1; j < n; j++ ){
tmp[m] = p[j]; //p[i]右边的所有点
tmp[m++].pos = j; //用于树状数组的离散化
}
sort( tmp, tmp+m, cmpp ); //根据极角排序,确保每一个访问过的点都处于当前点的下方
a = v.id;
memset( sum, 0, sizeof(sum) );
for( int j = 0; j < m; j++ ){
b = tmp[j].id;
under[a][b] = query( tmp[j].pos ); //正下方有多少个点
add( tmp[j].pos ); //当前点加入
under[b][a] = -under[a][b]; //反向为负
}
}
}
int query( int x, int y, int z )
{
tmp[0] = p[x]; tmp[1] = p[y]; tmp[2] = p[z];
sort( tmp, tmp+3, cmp );
int ans = abs( under[tmp[0].id][tmp[1].id] + under[tmp[1].id][tmp[2].id] + under[tmp[2].id][tmp[0].id] );
// long long 坑爹啊
long long a1 = (tmp[1].x-tmp[0].x), a2 = (tmp[2].y-tmp[0].y), a3 = (tmp[1].y-tmp[0].y), a4 = (tmp[2].x-tmp[0].x);
if( a1 * a2 > a3 * a4 ) //其中某一条线段的右端点是一条垂直于x轴的线段, 而多算了一个点
ans--;
return ans;
}
int main()
{
int m, x, y, z, i;
int t = 0, T;
scanf( "%d", &T );
while( T-- ){
scanf( "%d", &n );
for( i = 0; i < n; i++ ){
scanf( "%d%d", &p[i].x, &p[i].y );
p[i].id = i;
}
init();
sort( p, p+n, cmpid );
scanf( "%d", &m );
printf( "Case %d:\n", ++t );
for( ; m > 0; m-- ){
scanf( "%d%d%d", &x, &y, &z );
printf( "%d\n", query( x, y, z ) );
}
}
}