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

    题意:

    在平面坐标系上给定n个不同的整点,我们称从这n个点中选择6个不同的点所组成的有序六元组(A,B,C,D,E,F)是一条“鱼”,当且仅当:

    1. $AB=AC,BD=CD,DE=DFAB=AC,BD=CD,DE=DF$(身形要对称)
    2. $angle BAD,angle BDA,angle CAD,angle CDA<90^circ$(脑袋和屁股显然不能是凹的)
    3. $angle ADE,angle ADF>90^circ$

    求这n个点能组成多少条鱼。点集相同而顺序不同的算多种方案。

    $nleq 1000$。

    题解:

    首先发现一个鱼的核心就是线段AD,在确定AD后,BC与EF互不影响。

    所以我们考虑$O(n^{2})$枚举AD,然后预处理出BC与EF的贡献。

    EF看起来比较容易,我们只需要将某个半平面内的点按dis分类并维护组合数,可以直接支持单点修改。

    先极角排序,合法的点一定是一段区间,我们考虑维护双指针$l$和$r$,并将序列延长一倍以避免跳出边界(由于按极角序枚举A,一定不会跳出2n)。

    以D为原点,DA为x轴正方向建系,那么每次$r$要跑到第四象限的第一个点,$l$要跑到第二象限的第一个点。用叉积和点积判一下即可。

    BC看起来比较麻烦,需要满足BC垂直于AD且BC的中点在AD上。那么考虑预处理所有线段BC并维护这两个东西。

    维护斜率时尽量用整数,只需要将$(x,y)$处理成$(frac{x}{gcd(x,y)},frac{y}{gcd(x,y)})$。注意特判同一条直线正负两边的情况。

    维护中点时也尽量用整数,直接将坐标和$ imes 2$就行了。

    然后将线段BC排序,第一关键字是斜率,然后如果两个BC对应的直线AD相同(即这两个BC中点的连线垂直于它们自己,可以点积判)就按中点坐标大小排序,否则按它们对应AD的顺序排序(正反无所谓)。

    当然也可以将每个BC对应的AD用斜率和截距表示出来并用map离散化。不过我这样写完调不出来了。

    然后每次只需要$lowerbound$一下就可以算答案了。

    复杂度$O(n^{2}log{n})$,细节巨多。我考场上必拿0分。

    套路:

    • 降低枚举复杂度:考虑哪些是必须枚举的,哪些是枚举完必须枚举的之后可以预处理的。
    • 写计算几何时:思路清晰优先于代码简洁。

    代码:

    #include<bits/stdc++.h>
    #define maxn 1000005
    #define maxm 500005
    #define inf 0x7fffffff
    #define ll long long
    #define rint register ll
    #define debug(x) cerr<<#x<<": "<<x<<endl
    #define fgx cerr<<"--------------"<<endl
    #define dgx cerr<<"=============="<<endl
    
    using namespace std;
    map<ll,ll> siz;
    
    struct point{
        ll x,y;
        point operator+(const point b)const{return (point){x+b.x,y+b.y};}
        point operator-(const point b)const{return (point){x-b.x,y-b.y};}
        ll operator*(const point b)const{return x*b.y-y*b.x;}
        bool operator<(const point b)const{return x==b.x?y<b.y:x<b.x;}
    }P[maxn],tp[maxn];
    inline ll dot(point a,point b){return a.x*b.x+a.y*b.y;}
    struct line{
        point dir,mid;
        bool operator<(const line b)const{
            if(dir.x!=b.dir.x || dir.y!=b.dir.y) return dir<b.dir;
            else if(dot(dir,mid)!=dot(b.dir,b.mid)) return dot(dir,mid)<dot(b.dir,b.mid);
            else return mid<b.mid;
        }
    }L[maxn];
    
    inline ll read(){
        ll x=0,f=1; char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        return x*f;
    }
    
    inline ll dis(point a){return a.x*a.x+a.y*a.y;}
    inline bool xx(point a){return (a.y==0)?(a.x<0):(a.y>0);}
    inline bool cmp(point a,point b){return (xx(a)!=xx(b))?(xx(a)<xx(b)):(a*b>0);}
    inline ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
    inline point solve(point a){
        ll k=gcd(a.x,a.y); a.x/=k,a.y/=k;
        if(a.x<0) a.x=-a.x,a.y=-a.y;
        if(a.x==0) a.y=abs(a.y);
        return a;
    }
    
    int main(){
        ll n=read(),tot=0,ans=0;
        for(ll i=1;i<=n;i++) 
            P[i].x=read(),P[i].y=read();
        for(ll i=1;i<=n;i++)
            for(ll j=i+1;j<=n;j++){
                point k=solve(P[i]-P[j]);
                L[++tot]=(line){k,P[i]+P[j]};
            }
        sort(L+1,L+1+tot);
        for(ll i=1;i<=n;i++){
            point D=P[i]; siz.clear();
            ll l=1,r=1,cnt=0,num=0;
            for(ll j=1;j<=n;j++) 
                if(j!=i) tp[++cnt]=P[j]-D;
            sort(tp+1,tp+1+cnt,cmp);
            for(ll j=cnt+1;j<=cnt*2;j++) tp[j]=tp[j-cnt];
            for(ll j=1;j<=cnt;j++){
                point A=tp[j];
                while(dot(A,tp[r])<0 || A*tp[r]>0 || (A*tp[r]==0 && r<=cnt)) num+=(siz[dis(tp[r++])]++);
                while(l<r && dot(A,tp[l])>=0) num-=(--siz[dis(tp[l++])]);
                if(num){
                    point k=solve((point){-A.y,A.x});
                    point lp=D+D,rp=D+D+A+A;
                    if(rp<lp) swap(lp,rp);
                    ll s1=lower_bound(L+1,L+1+tot,(line){k,rp})-L;
                    ll s2=upper_bound(L+1,L+1+tot,(line){k,lp})-L;
                    ans+=4ll*(s1-s2)*num;
                }
            }
        }
        printf("%lld
    ",ans);
        return 0;
    }
  • 相关阅读:
    C#使用System.IO.Path获取文件路径、文件名
    C# 中的await
    深度学习笔记
    单例模式
    hbase的写和读,大合并和小合并
    自定义kafka Sink
    combineByKey
    spark练习题
    sparkonhbase
    HDFS只支持文件append操作, 而依赖HDFS的HBase如何完成增删改查功能
  • 原文地址:https://www.cnblogs.com/YSFAC/p/13154529.html
Copyright © 2011-2022 走看看