luogu P5294 [HNOI2019]序列

传送门

这题真的牛皮,还好考场没去刚(

这题口胡起来真的简单

首先枚举D点,然后对其他所有点按极角排序,同时记录到D的距离.然后按照极角序枚举A,那么鱼尾的两个点的极角范围就是A关于D对称的那个向量,然后左右各\(\frac{\pi}{2}\),因为A的极角增大,区间也会往后移,然后问题就是一个范围内同距离点对数,学过莫队的都会吧(逃

然后处理BC,一对合法的BC,首先要和AD垂直,然后BC中点要落在线段AD(不含端点)上,那么,BC中垂线必须唯一(中垂线的斜率和截距唯一),并且BC对应的中点的坐标范围要夹在A和D之间,然后预处理所有线段,按中垂线斜率,截距以及中点的x,y坐标之和三维度排序,每次有个AD,就能直接二分找到合法区间,然后直接算数量

注意BC,EF之间可以反过来,所以最后答案*4

代码仅供参考

// luogu-judger-enable-o2
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<queue>
#include<map>
#include<set>
#define LL long long
#define db long double

using namespace std;
const int N=1000+10;
const db eps=1e-13,pi=acos(-1);
int rd()
{
    int x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*w;
}
struct point
{
    db x,y;
    point(){}
    point(db nx,db ny){x=nx,y=ny;}
    point operator - (const point &bb) const {return point(x-bb.x,y-bb.y);}
    db operator * (const point &bb) const {return x*bb.x+y*bb.y;}
    db operator ^(const point &bb) const {return x*bb.y-y*bb.x;}
}a[N],b[N],fk;
db sq(db x){return x*x;}
db dis(point aa,point bb){return sqrt(sq(aa.x-bb.x)+sq(aa.y-bb.y));}
db ang(point aa,point bb){return atan2(bb.y-aa.y,bb.x-aa.x);}
int n,m,bk[N];
LL ans,sb[N],tb,tc,na;
struct node
{
    db dx,dy,b,x;
    bool operator < (const node &bb) const
    {
        if(fabs(dy*bb.dx-dx*bb.dy)>eps) return dy*bb.dx<dx*bb.dy;
        if(!dx&&fabs(b-bb.b)>eps) return b<bb.b;
        else if(fabs(b*bb.dx-bb.b*dx)>eps) return b*bb.dx<bb.b*dx;
        return x<bb.x;
    }
}p2[N*N];
struct nn
{
    db a,x,y;LL d;
    bool operator < (const nn &bb) const {return a<bb.a;}
}vc[N<<1];

int main()
{
    n=rd();
    for(int i=1;i<=n;++i)
    {
        int x=rd(),y=rd();
        a[i]=point(x,y);
    }
    for(int i=1;i<=n;++i)
        for(int j=i+1;j<=n;++j)
        {
            db dx=-(a[i].y-a[j].y),dy=a[i].x-a[j].x,k=-(a[i].x-a[j].x+(fabs(a[i].x-a[j].x)<eps?eps:0))/(a[i].y-a[j].y+(fabs(a[i].y-a[j].y)<eps?eps:0));
            if(dx<0) dx=-dx,dy=-dy;
            if(fabs(dx)<eps) dy=1;
            db mx=(a[i].x+a[j].x)/2,my=(a[i].y+a[j].y)/2,bb=dx*my-dy*mx;
            p2[++m]=(node){dx,dy,dx?bb:mx,fabs(k+1)>eps?mx+my:mx};
        }
    sort(p2+1,p2+m+1);
    p2[41]<p2[42];
    for(int i=1;i<=n;++i)
    {
        memset(bk,0,sizeof(bk)),na=tb=tc=0;
        for(int j=1;j<=n;++j)
            if(i!=j)
            {
                LL sx=floor(a[i].x+0.5),sy=floor(a[i].y+0.5),tx=floor(a[j].x+0.5),ty=floor(a[j].y+0.5);
                vc[++tc]=(nn){ang(a[i],a[j]),a[j].x,a[j].y,(sx-tx)*(sx-tx)+(sy-ty)*(sy-ty)};
                sb[++tb]=(sx-tx)*(sx-tx)+(sy-ty)*(sy-ty);
            }
        sort(vc+1,vc+tc+1);
        sort(sb+1,sb+tb+1),tb=unique(sb+1,sb+tb+1)-sb-1;
        for(int j=1;j<=tc;++j) vc[j].d=lower_bound(sb+1,sb+tb+1,vc[j].d)-sb;
        for(int j=1;j<=tc;++j) vc[j+tc]=vc[j],vc[j+tc].a+=pi+pi;
        for(int j=1,l=1,r=0;j<=tc;++j)
        {
            while(r<tc+tc&&vc[r+1].a<vc[j].a+1.5*pi-eps) ++r,++bk[vc[r].d],na+=bk[vc[r].d]-1;
            while(vc[l].a<vc[j].a+0.5*pi+eps) na-=bk[vc[l].d]-1,--bk[vc[l].d],++l;
            db dx=a[i].x-vc[j].x,dy=a[i].y-vc[j].y,k=(a[i].y-vc[j].y+(fabs(a[i].y-vc[j].y)<eps?eps:0))/(a[i].x-vc[j].x+(fabs(a[i].x-vc[j].x)<eps?eps:0));
            if(dx<0) dx=-dx,dy=-dy;
            if(fabs(dx)<eps) dy=1;
            db bb=a[i].y*dx-a[i].x*dy,ll=fabs(k+1)>eps?a[i].x+a[i].y:a[i].x,rr=fabs(k+1)>eps?vc[j].x+vc[j].y:vc[j].x;
            if(ll>rr) swap(ll,rr);
            int sl=upper_bound(p2+1,p2+m+1,(node){dx,dy,dx?bb:a[i].x,ll+eps})-p2,sr=lower_bound(p2+1,p2+m+1,(node){dx,dy,dx?bb:a[i].x,rr-eps})-p2-1;
            ans+=na*(sr-sl+1);
        }
    }
    printf("%lld\n",ans<<2);
    //awsl
    return 0;
}
上一篇:Latex内置转矢量图功能


下一篇:c++ code:(2)function