FZU 1973 How many stars ( 计算三角形内点的个数 )

题意: 给出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 ) );
		}
	}
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值