题解:P9284 [AGM 2023 资格赛] 海盗
wangtairan114 · · 题解
扫描线加线段树好题。
题意
给
思路
看到矩形覆盖容易想到扫描线。将
以考虑横向线段为例,我们维护一棵线段树,遍历衡坐标,则每个矩形的上边界可以视为一段区间加一。同理,下边界是区间减一操作。区间操作完进行查询即可。
问题在于如何通过区间查询快速获取线段覆盖情况。如果一段线段被部分覆盖或没有覆盖,则区间最小值为零。而如果线段部分覆盖或全部覆盖,区间最大值不为零。因此,我们只需维护区间最值即可。
时间复杂度
代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define ll long long
#define sc scanf
#define pr printf
#define v1 first
#define v2 second
#define f(nm1,nm2,nm3) for(int nm1=nm2; nm1<= nm3; nm1++)
#define lowbit(x) (x&(-x))
#define lson k*2,l,mid
#define rson k*2+1,mid+1,r
#define mid ((l+r)>>1)
int n,m;
struct bin{//线段树封装
int maxn[400005],minn[400005],lazy[400005];
void build(int k,int l,int r){//建树
lazy[k]=0;
if(l==r){
maxn[k]=0;
minn[k]=0;
return;
}
maxn[k]=max(maxn[k*2],maxn[k*2+1]);
minn[k]=min(minn[k*2],minn[k*2+1]);
}
void push_down(int k,int l,int r){//下传懒标记
if(!lazy[k])
return;
maxn[k*2]+=lazy[k];
minn[k*2]+=lazy[k];
maxn[k*2+1]+=lazy[k];
minn[k*2+1]+=lazy[k];
lazy[k*2]+=lazy[k];
lazy[k*2+1]+=lazy[k];
lazy[k]=0;
}
void modify(int k,int l,int r,int lbor,int rbor,int val){//区间加法
if(lbor<=l&&r<=rbor){
maxn[k]+=val;
minn[k]+=val;
lazy[k]+=val;
return;
}
push_down(k,l,r);
if(mid>=lbor){
modify(lson,lbor,rbor,val);
}
if(mid<rbor){
modify(rson,lbor,rbor,val);
}
maxn[k]=max(maxn[k*2],maxn[k*2+1]);
minn[k]=min(minn[k*2],minn[k*2+1]);
}
pair<int,int> query(int k,int l,int r,int lbor,int rbor){//查询区间最值
if(lbor<=l&&r<=rbor)
return {maxn[k],minn[k]};
push_down(k,l,r);
pair<int,int> ans={0,INF};
if(lbor<=mid){
auto res=query(lson,lbor,rbor);
ans.v1=max(ans.v1,res.v1);
ans.v2=min(ans.v2,res.v2);
}
if(rbor>mid){
auto res=query(rson,lbor,rbor);
ans.v1=max(ans.v1,res.v1);
ans.v2=min(ans.v2,res.v2);
}
return ans;
}
}b1,b2;
int b;
vector<pair<pair<int,int>,int>> vv1[100005],vv2[100005],q1[100005],q2[100005];
int ans[200005];
#undef x1
#undef y1
int main()
{
sc("%d%d",&n,&m);
sc("%d",&b);
for(int i=1,x1,y1,x2,y2; i <= b; i++){
sc("%d%d%d%d",&x1,&y1,&x2,&y2);
vv1[x1].push_back({{y1,y2},1});//x1的位置区间加1
vv1[x2+1].push_back({{y1,y2},-1});//x2+1的位置区间减1
vv2[y1].push_back({{x1,x2},1});//同理
vv2[y2+1].push_back({{x1,x2},-1});
}
int s;
sc("%d",&s);
for(int i=1,op,x,y,z; i <= s; i++){
sc("%d%d%d%d",&op,&x,&y,&z);
if(op==1)
q1[x].push_back({{y,z},i});//查询操作
else
q2[x].push_back({{y,z},i});
}
b1.build(1,1,m);
for(int i=1; i <= n; i++){//维护衡坐标
for(auto y:vv1[i])
b1.modify(1,1,m,y.v1.v1,y.v1.v2,y.v2);//区间修改
for(auto y:q1[i]){
auto res=b1.query(1,1,m,y.v1.v1,y.v1.v2);//区间查询
if(res.v1==0){
ans[y.v2]=0;//没有被覆盖
}
else if(res.v2==0){
ans[y.v2]=1;//部分覆盖
}
else{
ans[y.v2]=2;//完全覆盖
}
}
}
b2.build(1,1,n);
for(int i=1; i <= m; i++){
for(auto y:vv2[i])
b2.modify(1,1,n,y.v1.v1,y.v1.v2,y.v2);
for(auto y:q2[i]){
auto res=b2.query(1,1,n,y.v1.v1,y.v1.v2);
if(res.v1==0){
ans[y.v2]=0;
}
else if(res.v2==0){
ans[y.v2]=1;
}
else{
ans[y.v2]=2;
}
}
}
for(int i=1; i <= s; i++){//输出
if(ans[i]==0){
pr("MISS\n");
}
else if(ans[i]==1){
pr("HIT\n");
}
else{
pr("SUNK\n");
}
}
return 0;
}