zoukankan      html  css  js  c++  java
  • [POI2008]TROTriangles

    X.[POI2008]TRO-Triangles

    本题介绍两种做法。

    一种是我自己的做法:

    考虑某\(\triangle ABC\)\(2S_{\triangle ABC}=\Big|\vec{AB}\times\vec{AC}\Big|=\Big|(\vec{B}-\vec{A})\times(\vec{C}-\vec{A})\Big|\)

    于是我们考虑固定某个\(A\),这时就可以令\(\vec{B'}=\vec{B}-\vec{A},\vec{C'}=\vec{C}-\vec{A}\),这样我们只需要求出所有的\(\Big|\vec{B'}\times\vec{C'}\Big|\)即可。

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

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

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

    复杂度\(O(n^2\log n)\)。鉴于此题极度卡常,最后挂掉了。

    代码:

    #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;
    } 
    

  • 相关阅读:
    8月3日云栖精选夜读:LSF-SCNN:一种基于CNN的短文本表达模型及相似度计算的全新优化模型
    linux(centos)下安装PHP的PDO扩展
    Linux中find常见用法示例
    01 编译原理概述
    20145221 《信息安全系统设计基础》第5周学习总结
    20145221 《信息安全系统设计基础》第4周学习总结
    爱春秋之戏说春秋 Writeup
    20145221 《信息安全系统设计基础》第3周学习总结
    20145221 《信息安全系统设计基础》第2周学习总结
    20145221 《信息安全系统设计基础》第1周学习总结
  • 原文地址:https://www.cnblogs.com/Troverld/p/14619316.html
Copyright © 2011-2022 走看看