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

· · 题解

算法:计算几何+双指针

首先,看数据范围可知,不能够去枚举每一条线段,两两判断是否相交。但是,本题较为特殊的是,满足所有敌人在x轴上方,所有发射源和激光塔在x轴下方。于是

#include<bits/stdc++.h>
const double pi=3.1415926535;
using namespace std;
void read(int &x)
{
    char c=getchar();x=0;int f=1;
    while(!isdigit(c))f=c=='-'?-f:f,c=getchar();
    while(isdigit(c))x=x*10+c-'0',c=getchar();x*=f;
}
void read(long long &x)
{
    char c=getchar();x=0;int f=1;
    while(!isdigit(c))f=c=='-'?-f:f,c=getchar();
    while(isdigit(c))x=x*10+c-'0',c=getchar();x*=f;
}
const int N=1005;
int D,S,T;
struct point
{
    long long x,y;
    double angle;
    point(){}
    point(long long x0,long long y0){x=x0;y=y0;angle=atan2(y,x);};
    void init(){read(x);read(y);angle=atan2(y,x);}
    point operator-(const point &a)const{return point(x-a.x,y-a.y);}
}d[N],s[N],t[N];
typedef point vec;
long long cross(vec a,vec b){return a.x*b.y-a.y*b.x;}
const int onseg=0,onl=1,onr=-1;
int ccw(vec a,vec b)
{
    if(cross(a,b)>(long long)0)return onl;
    else if(cross(a,b)<(long long)0)return onr;
    else return onseg;
}
bool operator<(vec a,vec b){return a.angle>b.angle;}
long long ans;
struct ray
{
    vec vc;
    bool type;
}v[N<<2];
bool sign(long long x){return x>=0;}
bool cmp(ray a,ray b){return a.vc<b.vc;}
int sd[N<<2],st[N<<2],sum[N<<2];
void solve(point p)
{
    for(int i=1;i<=D;i++)v[i]={d[i]-p,0};
    for(int i=1;i<=T;i++)v[D+i]={t[i]-p,1};
    sort(v+1,v+1+D+T,cmp);
    for(int i=1;i<=D+T;i++)v[i+D+T]=v[i];
    for(int i=1;i<=(D+T)*2;i++)
    {
        st[i]=st[i-1];
        sd[i]=sd[i-1];
        sum[i]=sum[i-1];
        if(v[i].type)
        {
            st[i]++;
            sum[i]+=sd[i];
        }
        else sd[i]++;
    }
    for(int i=1,j=1;i<=D+T;i++)if(v[i].type)
    {
        j=max(j,i);
        while(j+1<i+D+T&&ccw(v[i].vc,v[j+1].vc)<=0)j++;
        ans+=sum[j]-sum[i]-sd[i]*(st[j]-st[i]);
    }
}
int main(){
    read(D);
    for(int i=1;i<=D;i++)d[i].init();
    read(S);
    for(int i=1;i<=S;i++)s[i].init();
    read(T);
    for(int i=1;i<=T;i++)t[i].init();
    for(int i=1;i<=S;i++)solve(s[i]);
    cout<<ans<<endl;
    return 0;
}

我写这道题时,因为找不到讲的比较详细的题解而走了许多弯路,花了两天才想出统计答案的办法,希望这篇题解能对读者有帮助。