题解 P5545 【[JSOI2016]炸弹攻击2】

xtx1092515503

2020-11-03 08:40:54

Solution

# [Portal](https://www.luogu.com.cn/problem/P5545) 直接统计显然是不行的($n^4$),我们必须得固定一个量,求出满足限制的其它三元组的个数。我选择固定发射源,并且将所有敌人与激光塔按照与当前发射源(之后称其为源点)的向量的倾角排序。 我们考虑符合条件的三元组个数。显然,因为激光塔全在 $x$ 轴下方,敌人全在 $x$ 轴上方,故在射线 $(\text{源点},\text{敌人})$ 两侧的所有激光塔对都可以与该敌人构成合法三元组。 但需要注意的是,当激光塔对与该射线无交点时,这个三元组是不合法的;稍加观察即可发现,有交点的充要条件是两激光塔的向量夹角 $<\pi$。(注意到它强调了没有三点共线的情况,这就使得很多边界情况不用处理。想要研究如何处理边界情况的,推荐[这道题](https://www.luogu.com.cn/problem/P3476),尽管此种方法会在该题中因为常数过大而T掉)。 于是我们考虑再固定一个激光塔 $j$,则所有在其前方 $\pi$ 范围内的其它激光塔都是合法的。我们可以通过 two-pointers 求出其前方最后一个合法激光塔 $k$。 在 two-pointers 的过程中,我们可以记录以当前节点为端点时的答案 $sum$、合法区间中的激光塔数 $st$、合法区间中的敌人数 $se$。 当一个激光塔进入区间时,$st$ 增加,同时 $sum$ 增加 $se$(该激光塔、$j$、$se$ 中每个敌人,均构成一个三元组)。当一个敌人进入区间时,$se$ 增加。当一个激光塔离开区间时,$st$ 减少。当一个敌人离开区间时,$se$ 减少,并且 $sum$ 减少 $st$(理由同上)。 时间复杂度 $O\Big(S(D+T)\log (D+T)\Big)$,瓶颈在于排序。 代码: ```cpp #include<bits/stdc++.h> using namespace std; typedef long long ll; const double pi=acos(-1); int E,T,B; ll res; 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);} friend Vector operator -(const Vector &u,const Vector &v){return Vector(u.x-v.x,u.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);} 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);} }ene[810],tow[810],bea[810]; pair<Vector,bool>p[1610]; int main(){ scanf("%d",&E); for(int i=1;i<=E;i++)ene[i].read(); scanf("%d",&B); for(int i=1;i<=B;i++)bea[i].read(); scanf("%d",&T); for(int i=1;i<=T;i++)tow[i].read(); for(int o=1;o<=B;o++){ int m=0; for(int i=1;i<=T;i++)p[m++]=make_pair(tow[i]-bea[o],0); for(int i=1;i<=E;i++)p[m++]=make_pair(ene[i]-bea[o],1); sort(p,p+m); int sum=0,se=0,st=0,len=0; for(int j=0,k=0;j<m;){ while(len<m&&(p[j].first&p[k].first)>=0){ if(p[k].second==1)se++; else sum+=se,st++; len++,k=(k+1)%m; } if(p[j].second==0)res+=sum; if(p[j].second==0)st--; else sum-=st,se--; j++,len--; } } printf("%lld\n",res); return 0; } ```