P5545 [JSOI2016] 炸弹攻击2

题意

给出 \(3\) 类点 \(S,T,D\),分别有 \(S,T,D\) 个。求满足条件的四元组 \((i,j,k,l)\) 的数量,满足线段 \(S_iD_l\) 和线段 \(T_jT_k\) 相交。
\(S,T,D\le800\),类型 \(S\)\(T\) 的点 \(y\) 坐标均小于 \(0\)

思路

枚举一个 \(S\) 类点,以祂为原点建系,把所有 \(T\)\(D\) 类点按照与 \(x\) 正半轴的夹角排序,那么两个 \(T\) 类点和任意一个夹在祂们之间的 \(D\) 类点会构成一个合法组合。暴力枚举这两个 \(T\) 类点,用前缀和计算数量。
注意以下几点:

  1. 夹在两个 \(T\) 类点之间的点是祂们两个所成的劣交所对的点,而不是与 \(x\) 正半轴的夹角大小在两个 \(T\) 类点之间的点。
  2. 反三角函数常数大,可以提前计算好,不然会 \(TLE\)

代码

// Problem: P5545 [JSOI2016] 炸弹攻击2
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P5545
// Memory Limit: 500 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T&x){
        x=0;char c=getchar();bool f=0;
        while(!isdigit(c)) c=='-'?f=1:0,c=getchar();
        while(isdigit(c)) x=x*10+c-'0',c=getchar();
        f?x=-x:0;
    }
    template<typename T>
    inline void write(T x){
        if(x==0){putchar('0');return ;}
        x<0?x=-x,putchar('-'):0;short st[50],top=0;
        while(x) st[++top]=x%10,x/=10;
        while(top) putchar(st[top--]+'0');
    }
    inline void read(char&c){c=getchar();while(isspace(c)) c=getchar();}
    inline void write(char c){putchar(c);}
    inline void read(string&s){s.clear();char c;read(c);while(!isspace(c)&&~c) s+=c,c=getchar();}
    inline void write(string s){for(int i=0,len=s.size();i<len;i++) putchar(s[i]);}
    template<typename T>inline void write(T*x){while(*x) putchar(*(x++));}
    template<typename T,typename...T2> inline void read(T&x,T2&...y){read(x),read(y...);}
    template<typename T,typename...T2> inline void write(const T x,const T2...y){write(x),putchar(' '),write(y...),sizeof...(y)==1?putchar('\n'):0;}
}using namespace IO;
#define LL long long
const int maxn=810;
const double PI=3.14159265358979323846;
int D,S,T,ans,sum[maxn*2];
struct point{int x,y;}d[maxn],s[maxn],t[maxn];
double getang(point d){return atan2(d.y,d.x);}
struct node{
    point d;
    int type;
    double ange;
    bool operator<(const node t)const{return ange<t.ange;}
    void init(){ange=getang(d);}
};
vector<node>vt;
inline void read(point&x){read(x.x,x.y);}
point getvec(point a,point b){return{b.x-a.x,b.y-a.y};}
LL operator*(const point a,const point b){return 1ll*a.x*b.y-1ll*b.x*a.y;}
int calcsum(int l,int r){
    if(l>r) return 0;
    if(l==0) return sum[r];
    return sum[r]-sum[l-1];
}
signed main(){
    read(D);
    for(int i=1;i<=D;i++) read(d[i]);
    read(S);
    for(int i=1;i<=S;i++) read(s[i]);
    read(T);
    for(int i=1;i<=T;i++) read(t[i]);
    LL ans=0;
    for(int i=1;i<=S;i++){
        vt.clear();
        for(int j=1;j<=T;j++) vt.push_back({getvec(s[i],t[j]),1}),vt.back().init();
        for(int j=1;j<=D;j++) vt.push_back({getvec(s[i],d[j]),2}),vt.back().init();
        sort(vt.begin(),vt.end());
        sum[0]=(vt[0].type==2);
        for(int i=1;i<vt.size();i++) sum[i]=sum[i-1]+(vt[i].type==2);
        for(int i=0;i<vt.size();i++) for(int j=i+1;j<vt.size();j++){
            if(vt[i].type!=1) break;
            if(vt[j].type!=1) continue;
            if(vt[j].ange-vt[i].ange>PI) ans+=calcsum(0,i)+calcsum(j,vt.size()-1);
            else ans+=calcsum(i,j);
        }
    }
    write(ans);
    return 0;
}
posted @ 2026-08-10 16:05  Link-Cut_Trees  阅读(24)  评论(0)    收藏  举报