洛谷P3122 [USACO15FEB]圈住牛Fencing the Herd(计算几何+CDQ分治)

本文探讨了一种利用CDQ分治策略优化凸包算法的方法,以解决所有点位于特定直线同一侧的问题。通过将点集分为两部分,分别构建凸包并更新答案,实现了高效求解。文章详细解释了算法流程,并提供了具体实现代码。

题面

传送门

题解

题目转化一下就是所有点都在直线\(Ax+By-C=0\)的同一侧,也就可以看做所有点代入\(Ax+By-C\)之后的值符号相同,我们只要维护每一个点代入直线之后的最大值和最小值,看看每条直线的最大最小值符号是否相同就好了

以最大值为例,我们强制\(B\geq 0\),那么能令这条直线取到最大值的点一定在所有点的上凸壳上,我们把凸壳搞出来,然后把所有直线按斜率从小到大排序,一遍扫过去就可以更新答案了

然而对于每条直线把前面所有点做个凸包?那有个吉儿用我还不如暴力枚举来得快

我们考虑\(CDQ\)分治,每次把\([l,mid]\)之间的点做一个凸包,然后用来更新\([mid+1,r]\)之间的答案就行了

//minamoto
#include<bits/stdc++.h>
#define R register
#define ll long long
#define inf 1e18
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
ll read(){
    R ll res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
const int N=2e5+5;
struct node{
    int x,y;
    node(){}
    node(R int xx,R int yy):x(xx),y(yy){}
    inline node operator -(const node &b)const{return node(x-b.x,y-b.y);}
    inline ll operator *(const node &b)const{return 1ll*x*b.y-1ll*y*b.x;}
    inline bool operator <(const node &b)const{return x==b.x?y<b.y:x<b.x;}
}p[N],st[N];
struct query{
    ll a,b,c;node p;int id;
    query(){}
    query(R ll a,R ll b,R ll c,R node P,R int Id):a(a),b(b),c(c),p(P),id(Id){}
}c[N],las[N];
struct qwq{ll a,b,c;int op,id;}q[N];
inline bool cmp1(const query &x,const query &y){return x.p*y.p>=0;}
inline bool cmp2(const query &x,const query &y){return x.p*y.p<=0;}
int ans[N];ll mn[N],mx[N],vc[N],maxn,minn;int op[N],n,m,tot,top,cnt,t;
inline void upd(R int id,R int x,R int y){
//  printf("%d %d %d\n",id,x,y);
    R ll val=las[id].a*x+las[id].b*y-las[id].c;
    cmax(mx[id],val),cmin(mn[id],val);
}
void solve(int l,int r){
    if(l==r)return;
    int mid=(l+r)>>1;
    solve(l,mid),solve(mid+1,r),top=cnt=tot=0;
    fp(i,l,mid)if(q[i].op&1)p[++tot]=node(q[i].a,q[i].b);
    fp(i,mid+1,r)if(q[i].op&2)c[++cnt]=query(q[i].a,q[i].b,q[i].c,node(q[i].b,-q[i].a),q[i].id);
    if(!tot||!cnt)return;
    sort(p+1,p+1+tot),st[top=1]=p[1];
    fp(i,2,tot){
        while(top>1&&(p[i]-st[top-1])*(st[top]-st[top-1])>=0)--top;
        st[++top]=p[i];
    }
    sort(c+1,c+1+cnt,cmp1);
//    printf("%d %d %d\n",l,r,cnt);
//    fp(i,1,cnt)printf("%d\n",c[i].id);
    for(R int i=1,j=1;i<=top;++i){
        while(j<=cnt&&(i+1>top||(c[j].p*(st[i+1]-st[i]))>=0))
            upd(c[j].id,st[i].x,st[i].y),++j;
    }
    st[top=1]=p[1];
    fp(i,2,tot){
        while(top>1&&(p[i]-st[top-1])*(st[top]-st[top-1])<=0)--top;
        st[++top]=p[i];
    }
    sort(c+1,c+1+cnt,cmp2);
    for(R int i=1,j=1;i<=top;++i){
        while(j<=cnt&&(i+1>top||c[j].p*(st[i+1]-st[i])<=0))
            upd(c[j].id,st[i].x,st[i].y),++j;
    }
}
int main(){
//  freopen("testdata.in","r",stdin);
    n=read(),m=read(),maxn=-inf,minn=inf;
    fp(i,1,n)q[i].op=1,q[i].a=read(),q[i].b=read(),cmax(maxn,q[i].a),cmin(minn,q[i].a);
    t=n;
    fp(i,1,m)ans[i]=-1;
    fp(i,1,m){
        ++t,op[i]=q[t].op=read();
        if(q[t].op&1)q[t].a=read(),q[t].b=read(),cmax(maxn,q[t].a),cmin(minn,q[t].a);
        else{
            q[t].a=read(),q[t].b=read(),q[t].c=read();
            if(!q[t].b)ans[i]=(-1.0*q[t].c/q[t].a<minn||-1.0*q[t].c/q[t].a>maxn),--t;
            else{
                if(q[t].b<0)q[t].a=-q[t].a,q[t].b=-q[t].b,q[t].c=-q[t].c;
                mn[i]=inf,mx[i]=-inf,q[t].id=i;
                las[i].a=q[t].a,las[i].b=q[t].b,las[i].c=q[t].c;
            }
        }
    }
//    fp(i,1,t)printf("%d %d %lld %lld %lld\n",q[i].op,q[i].id,q[i].a,q[i].b,q[i].c);
    solve(1,t);
    fp(i,1,t)if(q[i].op&2)ans[q[i].id]=(mx[q[i].id]<0||mn[q[i].id]>0);
    fp(i,1,m)if(op[i]&2)puts(ans[i]?"YES":"NO");
    return 0;
}

转载于:https://www.cnblogs.com/bztMinamoto/p/10520710.html

### 解决方案 USACO 的题目 **P2895 Meteor Shower S** 是一道经典的 BFS(广度优先搜索)问题,涉及路径规划以及动态障碍物的处理。以下是关于此题目的 C++ 实现方法及相关讨论。 #### 1. 题目概述 贝茜需要在一个二维网格上移动到尽可能远的位置,同时避开由流星造成的破坏区域。每颗流星会在特定时间落在某个位置,并摧毁其周围的五个单元格(中心及其上下左右)。目标是最小化贝茜受到的风险并计算最短到达安全地点的时间[^5]。 --- #### 2. 关键算法思路 为了高效解决这个问题,可以采用以下策略: - 使用 **BFS(广度优先搜索)** 来模拟贝茜可能的行走路线。 - 动态更新地图上的危险区域,确保在每个时刻只考虑有效的威胁。 - 提前预处理所有流星的影响范围,减少冗余计算。 由于直接在每次 BFS 中调用 `boom` 函数可能导致性能瓶颈[^4],因此可以通过优化来降低复杂度。 --- #### 3. 优化建议 为了避免重复标记已知的危险区域,可以在程序初始化阶段完成如下操作: - 创建一个数组记录每个单位时间内哪些坐标会被流星影响。 - 将 BFS 和流星爆炸事件同步进行,仅在必要时扩展新的状态。 这种方法能够显著提升运行速度,尤其对于大规模输入数据(如 $ M \leq 50,000 $),效果尤为明显。 --- #### 4. C++ 示例代码实现 下面提供了一个高效的解决方案框架: ```cpp #include <bits/stdc++.h> using namespace std; const int MAXN = 1e6; int grid[1001][1001]; // 地图大小假设为合理范围内 bool visited[1001][1001]; queue<pair<int, pair<int, int>>> q; // 存储 {time, {x, y}} // 方向向量定义 vector<pair<int, int>> directions = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} }; void initializeGrid(int N, vector<tuple<int, int, int>>& meteors) { memset(grid, 0, sizeof(grid)); for(auto &[t, x, y] : meteors){ if(t >= N || t < 0) continue; // 超过最大时间或负数忽略 grid[x][y] = max(grid[x][y], t); for(auto &[dx, dy] : directions){ int nx = x + dx, ny = y + dy; if(nx >=0 && nx <1001 && ny>=0 && ny<1001){ grid[nx][ny] = max(grid[nx][ny], t); } } } } bool isValid(int time, int x, int y){ return !(grid[x][y] <= time); // 如果当前时间<=流星爆炸时间则不可通过 } int main(){ ios::sync_with_stdio(false); cin.tie(0); int T, X, Y; cin >> T >> X >> Y; vector<tuple<int, int, int>> meteors(T); for(int i=0;i<T;i++) cin >> get<0>(meteors[i]) >> get<1>(meteors[i]) >> get<2>(meteors[i]); initializeGrid(X*Y, meteors); memset(visited, false, sizeof(visited)); q.push({0,{X,Y}}); visited[X][Y]=true; while(!q.empty()){ auto current = q.front(); q.pop(); int currentTime = current.first; int cx = current.second.first, cy = current.second.second; if(isValid(currentTime,cx,cy)){ cout << currentTime; return 0; } for(auto &[dx,dy]:directions){ int nx=cx+dx,ny=cy+dy; if(nx>=0&&nx<1001&&ny>=0&&ny<1001&&!visited[nx][ny]){ if(isValid(currentTime,nx,ny)){ q.push({currentTime+1,{nx,ny}}); visited[nx][ny]=true; } } } } cout << "-1"; // 若无解返回-1 return 0; } ``` 上述代码实现了基于 BFS 的最优路径查找逻辑,并预先构建了流星影响的地图以加速查询过程。 --- #### 5. 进一步讨论 尽管本题的核心在于 BFS 及动态更新机制的应用,但在实际编码过程中仍需注意以下几个方面: - 输入规模较大时应选用快速 IO 方法(如关闭同步流 `ios::sync_with_stdio(false)` 并取消绑定 `cin.tie(NULL)`)。 - 对于超出边界或者无关紧要的数据点可以直接跳过处理,从而节省不必要的运算开销。 - 利用位掩码或其他压缩技术存储访问标志可进一步节约内存资源。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值