鱼死网破

题目描述

由于领土纠纷,胖头鱼联邦打算偷袭鱼头胖共和国。由于两国关系本身十分紧张,所以双方都在边界上部署了很多士兵。

两个国家非常大,可以抽象成二维平面上的两个无穷大区域,鱼头胖共和国在 x x x 轴上方,胖头鱼联邦在 x x x 轴下方, x x x 轴不属于任何一个国家。胖头鱼有 n n n 个士兵,鱼头胖有 m m m 个士兵,士兵都在自己领土内,任意两个士兵的坐标不会重合。

为了成功偷袭,胖头鱼必须先击倒鱼头胖的士兵。胖头鱼士兵可以使用高压水枪射击鱼头胖士兵,但是鱼头胖在自己领土上设置了 k k k 道防水墙,一个胖头鱼士兵能攻击鱼头胖士兵当且仅当两个士兵的连线上没有防水墙(包括顶点)。防水墙可以抽象成一条平行于 x x x 轴的线段,可以用一个三元组 ( a , b , c ) (a,b,c) (a,b,c) 表示,即这个防水墙的两个端点的坐标是 ( a , c ) , ( b , c ) (a,c),(b,c) (a,c),(b,c) ,保证这个没有士兵在这条线段上。

每个胖头鱼士兵想知道自己能攻击多少个鱼头胖士兵。你必须逐个回答胖头鱼士兵的问题。强制在线!

数据范围

n , m ≤ 1 0 5 n,m \le 10^5 n,m105 k ≤ 50 k \le 50 k50 ; 所有点坐标绝对值 ≤ 1 0 6 \le 10^6 106

题解

考虑每个 x x x 轴上方的点,对于它下方的墙的两个端点构成两条射线,把左端点的射线权值设为 1 1 1 ,右端点的射线权值设为 − 1 -1 1 ,对于每个 x x x 轴下方的点,找到每个墙的每个端点,每个端点上会有一些射线,对这个点有贡献的是在其左方的射线,故极角排序后二分即可。效率: O ( k ( n + m ) l o g n ) O(k(n+m)logn) O(k(n+m)logn)

代码

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1e5+5;
int n,k,m,op,t,w[105],sz[105];
struct O{
    int x,y;
    friend O operator - (O A,O B){
        return (O){A.x-B.x,A.y-B.y};
    }
    friend LL operator * (O A,O B){
        return 1ll*A.x*B.y-1ll*A.y*B.x;
    }
}a[N],b[105],nw;
struct P{O u;int i;}g[105];
vector<O>p[N];
bool cmp(P A,P B){
    LL v=(A.u-nw)*(B.u-nw);
    return v>0 || (v==0 && w[A.i]>w[B.i]);
}
bool Cmp(O A,O B){return A*B>0;}
int main(){
    cin>>n>>k>>m>>op;
    for (int i=1;i<=n;i++)
        scanf("%d%d",&a[i].x,&a[i].y);
    for (int i=1,x,y,z;i<=k;i++){
        scanf("%d%d%d",&x,&y,&z);
        w[++t]=1;b[t]=(O){x,z};
        w[++t]=-1;b[t]=(O){y,z};
    }
    for (int c,x,i=1;i<=n;i++){
        c=0;nw=a[i];x=0;
        for (int j=1;j<=t;j++)
            if (a[i].y>b[j].y)
                g[++c]=(P){b[j],j};
        sort(g+1,g+c+1,cmp);
        for (int j=1;j<=c;j++){
            if (x==0 || (x==1 && w[g[j].i]==-1))
                p[g[j].i].push_back(g[j].u-nw),sz[g[j].i]++;
            x+=w[g[j].i];
        }
    }
    for (int i=1;i<=t;i++)
        sort(p[i].begin(),p[i].end(),Cmp);
    for (int x,y,lst=0;m--;){
        scanf("%d%d",&x,&y);
        if (op) x^=lst,y^=lst;lst=0;
        for (int l,r,i=1;i<=t;i++){
            if (!sz[i]) continue;
            nw=(O){x,y}-b[i];
            l=-1;r=sz[i]-1;
            while(l<r){
                int mid=(l+r+1)>>1;
                if (((~w[i])?nw*p[i][mid]>0:nw*p[i][mid]>=0)) r=mid-1;
                else l=mid;
            }
            lst+=w[i]*(l+1);
        }
        lst=n-lst;printf("%d\n",lst);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值