zoukankan      html  css  js  c++  java
  • [HNOI2019]鱼

    Luogu5286

    (2019.4.14),新生第一题,改了(3)个小时

    题解-租酥雨,和出题人给的正解一模一样

    枚举(AD),分别考虑鱼身(BC)和鱼尾(EF)
    (E)(F)距离相等的判断要避免精度问题,可以离散化其平方以后用一种莫队思想搞
    关键是怎么枚举垂直,还要控制中点也在(AD)

    判垂直和平行的关键就是
    与向量((a,b))点积相同的点在一条垂直向量((a,b))的直线上,与向量((a,b))叉积相同的点在一条平行向量((a,b))的直线上

    然后加一些转化来限制鱼身的范围:
    中点在(AD)上;
    端点相对于直线(AD)在线段(AD)上(这样的取值是一个区间)
    手推(逆推)就比较简单了

    细节非常多,等以后学了计算几何,有时间再打一遍

    (0.)考场上没看清题
    (1.)有些(可能是少数)精度问题(不要先除)可以利用一些离散的方法避免;相应地,统计的方法也可以稍加变换

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef long long LL;
    const int INF=1e9+7;
    inline LL read(){
    	register LL x=0,f=1;register char c=getchar();
    	while(c<48||c>57){if(c=='-')f=-1;c=getchar();}
    	while(c>=48&&c<=57)x=(x<<3)+(x<<1)+(c&15),c=getchar();
    	return f*x;
    }
    
    const int N=1005;
    const double eps=1e-10;//这题坐标1e9,所以精度至少要1e-10
    const double Pi=acos(-1);
    
    struct Point{
    	LL x,y;double k;//极角
    	inline void cal(){k=atan2(y,x);}
    	inline bool operator< (const Point &b) const {return k<b.k;}
    }p[N],q[N<<1];
    
    struct Node{
    	LL a,b,c,d;
    	inline bool operator< (const Node &z) const{ //这个const不能少
    		if(a!=z.a) return a<z.a;
    		if(b!=z.b) return b<z.b;
    		if(c!=z.c) return c<z.c;
    		return d<z.d;
    	}
    }S[N*N];
    
    LL len[N],tmpl[N];int cnt[N];
    int n,m,Scnt,Lcnt;LL ans,sum;
    
    inline void add(int x){if(x>m) x-=m;sum+=cnt[len[x]];cnt[len[x]]++;}//这样实现非常优秀,避免了精度问题
    inline void del(int x){if(x>m) x-=m;cnt[len[x]]--;sum-=cnt[len[x]];}
    
    inline LL sqr(int x){return 1ll*x*x;}
    
    int main(){
    	n=read();
    	for(int i=1;i<=n;i++) p[i]=(Point){read(),read()};
    	for(int i=1;i<=n;i++)
    		for(int j=i+1;j<=n;j++){
    			LL a=p[i].x-p[j].x,b=p[i].y-p[j].y,d=__gcd(abs(a),abs(b));
    			a/=d,b/=d;
    			if(a<0) a=-a,b=-b;
    			if(a==0) b=abs(b);
    			LL P=1ll*a*(p[i].x+p[j].x)+1ll*b*(p[i].y+p[j].y);//相应的要预处理出P和Q的值
    			LL Q=1ll*a*p[i].y-1ll*b*p[i].x;
    			S[++Scnt]=(Node){a,b,P,Q};
    		}
    	sort(S+1,S+Scnt+1);
    	for(int i=1;i<=n;i++){
    		m=0;
    		for(int j=1;j<=n;j++){
    			if(i!=j) q[++m]=(Point){p[j].x-p[i].x,p[j].y-p[i].y},q[m].cal();
    		}
    		sort(q+1,q+m+1);
    		for(int j=m+1;j<=m+m;j++) q[j]=q[j-m],q[j].k+=2*Pi;//开两倍避免讨论
    		Lcnt=0;
    		for(int j=1;j<=m;j++) len[j]=tmpl[++Lcnt]=sqr(q[j].x)+sqr(q[j].y);
    		sort(tmpl+1,tmpl+Lcnt+1);
    		Lcnt=unique(tmpl+1,tmpl+Lcnt+1)-tmpl-1;
    		for(int j=1;j<=m;j++) len[j]=lower_bound(tmpl+1,tmpl+Lcnt+1,len[j])-tmpl;//把相同长度的编号设为相同
    		sum=0;
    		memset(cnt,0,sizeof cnt);
    		for(int j=1,p1=0,p2=0;j<=m;j++){//确定AD
    			while(q[p1+1].k+eps<q[j].k+1.5*Pi) add(++p1);
    			while(q[p2+1].k-eps<q[j].k+0.5*Pi) del(++p2);
    			LL a=-q[j].y,b=q[j].x,d=__gcd(abs(a),abs(b));//枚举BC:与AD垂直
    			a/=d,b/=d;
    			if(a<0) a=-a,b=-b;
    			if(a==0) b=abs(b);//这些都只是一种规定排序的方法
    			LL P=1ll*a*(p[i].x+(p[i].x+q[j].x))+1ll*b*(p[i].y+(p[i].y+q[j].y));//中点在直线AD上
    			LL L=1ll*a*p[i].y-1ll*b*p[i].x, R=1ll*a*(p[i].y+q[j].y)-1ll*b*(p[i].x+q[j].x);//两端点在AD范围内
    			if(L>R) swap(L,R);
    			ans+=sum*(lower_bound(S+1,S+Scnt+1,(Node){a,b,P,R}) - upper_bound(S+1,S+Scnt+1,(Node){a,b,P,L}));
    		}
    	}
    	printf("%lld
    ",ans<<2);
    }
    
  • 相关阅读:
    python——数据库操作
    【笔试】T实习生2014 总结
    【JS】Intermediate6:jQuery
    【JS】Intermediate5:Scope
    【JS】Intermediate4:JSON
    【JS】Intermediate3:AJAX
    【JS】Intermediate2:Events and Callbacks
    【JS】Intermediate1:The DOM
    【JS】Beginner9:Arrays
    【JS】Beginner8:Objects
  • 原文地址:https://www.cnblogs.com/lizehon/p/10706499.html
Copyright © 2011-2022 走看看