[POI2008]TRO-Triangles

X.[POI2008]TRO-Triangles

本题介绍两种做法。

一种是我自己的做法:

考虑某ABC2SABC=|AB×AC|=|(BA)×(CA)|

于是我们考虑固定某个A,这时就可以令B=BA,C=CA,这样我们只需要求出所有的|B×C|即可。

如果它没有前缀和,我们完全可以直接乘法分配律掉;但是套上绝对值,咋办呢?

我们考虑到,设θ表示BC的夹角(其中BC的逆时针方向),则只有在θ[0,π)时,绝对值内的东西才为正;而θ=θBθC,其中θBB的辐角。于是我们将所有东西依照辐角排序,并且用一个two-pointers统计对于某个B,所有有θC[θB,θB+π)C之和。然后直接叉积在一起即可。

最终答案要除以6,因为每个三角形都在A,B,C三个顶点被统计了三次,并且一开始的式子是2SABC

复杂度O(n2logn)。鉴于此题极度卡常,最后挂掉了。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m;
const double pi=acos(-1);
struct Vector{
	int x,y;
	Vector(int X=0,int Y=0){x=X,y=Y;}
	friend Vector operator +(const Vector &u,const Vector &v){return Vector(u.x+v.x,u.y+v.y);}
	void operator +=(const Vector &v){x+=v.x,y+=v.y;}
	friend Vector operator -(const Vector &u,const Vector &v){return Vector(u.x-v.x,u.y-v.y);}
	void operator -=(const Vector &v){x-=v.x,y-=v.y;}
	friend Vector operator *(const Vector &u,const int &v){return Vector(u.x*v,u.y*v);}
	friend Vector operator /(const Vector &u,const int &v){return Vector(u.x/v,u.y/v);}
	friend ll operator &(const Vector &u,const Vector &v){return 1ll*u.x*v.y-1ll*u.y*v.x;}//cross times
	friend ll operator |(const Vector &u,const Vector &v){return 1ll*u.x*v.x+1ll*u.y*v.y;}//point times
	double operator !()const{return atan2(y,x);}//the angle of a vector
	friend bool operator <(const Vector &u,const Vector &v){return !u<!v;}
	void read(){scanf("%d%d",&x,&y);}
	void print(){printf("(%d,%d)",x,y);}
}p[3010],q[3010];
typedef Vector Point;
ll res;
int main(){
	scanf("%d",&n);
	for(int i=0;i<n;i++)p[i].read();
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++)q[j]=p[j]-p[i];
		sort(q,q+n);
		m=1;for(int j=1;j<n;j++)if((q[j]&q[m-1])||(q[j]|q[m-1])<0)q[m++]=q[j];else q[m-1]+=q[j];
//		for(int j=0;j<m;j++)q[j].print();puts("");
		Vector sum;
		int len=0;
		for(int j=0,k=0;j<m;j++){
			if(!len)sum=sum+q[k],len++,k=(k+1)%m;
			for(;len<m&&(q[j]&q[k])>0;k=(k+1)%m)sum+=q[k],len++;
//			printf("%lld\n",q[j]&sum);
			res+=q[j]&sum;
			sum-=q[j],len--;
		}
	}
	res/=3;
	printf("%lld.%d\n",res>>1,(res&1)?5:0);
	return 0;
} 

一种是正解做法。

我们考虑对于每个三角形,我们只在它左下方的节点统计一次。方法很简单,一开始将所有顶点先按照x为第一关键字,y为第二关键字排一次序,之后每次只需要统计后缀即可。

然后,每个节点所统计的所有东西,都在其右侧;故只需要进行一遍极角排序,对于一个节点,它后方的所有东西,都有叉积>0。所有统计后缀和并进行叉积即可。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m;
const double pi=acos(-1);
struct Vector{
	int x,y;
	Vector(int X=0,int Y=0){x=X,y=Y;}
	friend Vector operator +(const Vector &u,const Vector &v){return Vector(u.x+v.x,u.y+v.y);}
	void operator +=(const Vector &v){x+=v.x,y+=v.y;}
	friend Vector operator -(const Vector &u,const Vector &v){return Vector(u.x-v.x,u.y-v.y);}
	void operator -=(const Vector &v){x-=v.x,y-=v.y;}
	friend Vector operator *(const Vector &u,const int &v){return Vector(u.x*v,u.y*v);}
	friend Vector operator /(const Vector &u,const int &v){return Vector(u.x/v,u.y/v);}
	friend ll operator &(const Vector &u,const Vector &v){return 1ll*u.x*v.y-1ll*u.y*v.x;}//cross times
	friend ll operator |(const Vector &u,const Vector &v){return 1ll*u.x*v.x+1ll*u.y*v.y;}//point times
	friend bool operator <(const Vector &u,const Vector &v){return (u&v)>0;}
	void read(){scanf("%d%d",&x,&y);}
	void print(){printf("(%d,%d)",x,y);}
}p[3010],q[3010];
typedef Vector Point;
ll res;
bool cmp(const Vector &u,const Vector &v){return (u.x==v.x)?u.y<v.y:u.x<v.x;}
int main(){
	scanf("%d",&n);
	for(int i=0;i<n;i++)p[i].read();
	sort(p,p+n,cmp);
	for(int i=0;i<n;i++){
		for(int j=i+1;j<n;j++)q[j]=p[j]-p[i];
		sort(q+i+1,q+n);
		for(int k=n-1;k>i;k--)res+=(q[k]&q[k+1]),q[k]+=q[k+1];
	}
	printf("%lld.%d\n",res>>1,(res&1)?5:0);
	return 0;
} 

posted @   Troverld  阅读(71)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示