2020ICPC小米网络选拔赛第二场 E题(Query of Square)
题面
给你一个只有垂直和平行边的n个点的多边形,给定m次询问,查询以这个点为正方形左下角点且该正方形能满足一直在多边形内部的最大正方形的边长是多少。
Solution
这里我给出一种和题解不一样的偏向几何的做法。
首先我们可以观察出只有这两种情况会卡住这个正方形,由于有\(x\)和\(y\)的限制,并不能很好的直接维护垂直和水平线段对于点的影响。
但是似乎我们能发现一点,由于这个图形是一个正方形,所以会对这个点产生影响的线段,是会与从当前这个点引出一条\(y=x\)的斜向上的向量相交。
由于对每个点查询满足这样的线段求\(min\)的复杂度接受不了,但是我们可以反过来处理,去用当前的线段更新其影响域内的点的最值。接下来就相当于用线段树维护最靠近左侧能影响到他的线段的x值或者y值。我们可以将每条线段两端点各引出一条\(y=x\)斜向下的向量,这两条向量之间的所有点都将是这条线段需要更新的内容,由于垂直和水平都需要维护,所以最后相当于这两次维护值的最小值即可。例如当前是垂直边情况,我们可以将询问点用截距的方式映射在\(y\)轴上,那么会对其产生影响的线段需要满足两个条件,第一个是这条线段两侧向量在\(y\)轴上的交点需要包含这个点对应在\(y\)轴上的交点,这个我们可以直接通过离散化加线段树解决,第二个是这条线需要在这个点的右侧,那我们可以通过扫描线的方式,可以将这条线段和询问点以x递减的方式一起排序在进行更新线段树上的每个点最靠近他的y的最小值,那么最后减去这个点本身的\(y\)值就是最大边长了,如此对水平线段的也进行一次即可。
但是这样还不够,因为我们可以发现一个线段真正的影响域应该是如图所示,我们这样考虑会缺少一些影响域没更新。
为了将缺失的影响域补回来,我们可以将垂直的线段在其上端点补一条往左侧无限延伸的水平线段,同样,将水平的线段右侧补一条往下无限延伸的垂直线段,跟之前的处理方式一样,这样我们就可以将这条线段的影响域真正的补齐了(红色为补冲线段,分别往左侧和往下侧无限延伸,保证覆盖到所有域内的点)
如果不这样处理会出现以下情况使得答案变大。当然这样处理完之后和之前扫描线处理方式略有不同,例如垂直线段,补齐后需要用水平的线段再来更新一次答案,我们将这些水平线段和点按照\(y\)进行从大到小排序,但是更新的时候需要用垂直线段的\(x\)去更新答案,因为实际影响的还是补充前的垂直线段的\(x\)。
我们需要共执行水平一次,垂直一次,水平补充的垂直线段一次和垂直线段补充的水平线段一次,共四次更新,最后取这四者对答案的最小值即可,总体复杂度任然\(O((n+m)log(n+m))\)。
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define MP make_pair
#define pb push_back
#define eb emplace_back
#define rl register ll
#define ri register int
#define rs register short
#define pi 3.1415926535898
#define lowbit(a) (a&(-a))
#define lson l,(l+r)/2,rt<<1
#define rson (l+r)/2+1,r,rt<<1|1
#define INF 0x3f3f3f3f
#define Min(a,b,c) min(a,min(b,c))
#define Max(a,b,c) max(a,max(b,c))
#define debug(x) cerr<<#x<<"="<<x<<"\n";
#define mst(a,b,c) std::memset(a,b,(c)*sizeof(int))
#define cpy(a,b,c) std::memcpy(a,b,(c)*sizeof(int))
//#pragma GCC optimize(3)
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
typedef double db;
typedef long long ll;
typedef long double ld;
typedef pair<int,int> P;
typedef pair<ll,ll> Pll;
typedef unsigned long long ull;
constexpr int seed=131;
constexpr ll LLMAX=2e18;
constexpr int MOD=1e9+7;
constexpr double eps=1e-8;
constexpr int MAXN=2e5+10;
constexpr int hmod1=0x48E2DCE7;
constexpr int hmod2=0x60000005;
inline ll sqr(ll x){ return x*x; }
inline int sqr(int x){ return x*x; }
inline double sqr(double x){ return x*x; }
inline int mul(int x,int y){ return 1ll*x*y%MOD; }
inline int sub(int x,int y){ return x>=y?x-y:MOD+x-y; }
inline int add(int x,int y){ return x+y>=MOD?x+y-MOD:x+y; }
inline int dcmp(double x){ if(fabs(x)<eps) return 0; return (x>0? 1: -1); }
inline ll qpow(ll a,ll n){ll sum=1;while(n){if(n&1)sum=sum*a%MOD;a=a*a%MOD;n>>=1;}return sum;}
ll gcd(ll a,ll b){ return b==0? a: gcd(b,a%b); }
ll exgcd(ll a,ll b,ll &x,ll &y){ ll d; (b==0? (x=1,y=0,d=a): (d=exgcd(b,a%b,y,x),y-=a/b*x)); return d; }
P lp[MAXN],s[MAXN],t[MAXN],p[MAXN];
int a[MAXN*10],tree[MAXN<<4],lz[MAXN<<4],ans[MAXN],is[MAXN],it[MAXN],ip[MAXN];
struct Event{
int x,l,r,b,id,y;
bool operator<(const Event &n)const{
if(x==n.x) return id>n.id;
return x>n.x;
}
}event[MAXN*10];
inline void pushdown(int rt){
if(lz[rt]!=INF){
lz[rt<<1]=min(lz[rt<<1],lz[rt]),lz[rt<<1|1]=min(lz[rt<<1|1],lz[rt]);
tree[rt<<1]=min(tree[rt<<1],lz[rt]),tree[rt<<1|1]=min(tree[rt<<1|1],lz[rt]);
lz[rt]=INF;
}
}
void build(int l,int r,int rt){
tree[rt]=lz[rt]=INF;
if(l!=r) build(lson),build(rson);
}
void update(int l,int r,int rt,int a,int b,int c){
if(a<=l&&r<=b){
lz[rt]=min(lz[rt],c);
tree[rt]=min(tree[rt],c);
}
else{
int mid=(l+r)>>1;
pushdown(rt);
if(a<=mid) update(lson,a,b,c);
if(mid<b) update(rson,a,b,c);
}
}
int query(int l,int r,int rt,int a){
if(l==r) return tree[rt];
else{
int mid=(l+r)>>1;
pushdown(rt);
if(a<=mid) return query(lson,a);
else return query(rson,a);
}
}
int main(void)
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF){
int cs=0,cnt=0;
a[cs++]=-INF,a[cs++]=INF;
for(int i=0;i<n;i++) scanf("%d%d",&lp[i].fi,&lp[i].se);
lp[n]=lp[0];
for(int i=0;i<n;i++){
s[i]=lp[i],t[i]=lp[i+1];
if(s[i].fi==t[i].fi&&s[i].se>t[i].se) swap(s[i],t[i]);
if(s[i].se==t[i].se&&s[i].fi<t[i].fi) swap(s[i],t[i]);
a[cs++]=s[i].se-s[i].fi,a[cs++]=t[i].se-t[i].fi;
}
for(int i=0;i<m;i++){
ans[i]=INF;
scanf("%d%d",&p[i].fi,&p[i].se);
a[cs++]=p[i].se-p[i].fi;
}
sort(a,a+cs);
cs=unique(a,a+cs)-a;
for(int i=0;i<n;i++){
is[i]=lower_bound(a,a+cs,s[i].se-s[i].fi)-a+1;
it[i]=lower_bound(a,a+cs,t[i].se-t[i].fi)-a+1;
}
for(int i=0;i<m;i++) ip[i]=lower_bound(a,a+cs,p[i].se-p[i].fi)-a+1;
cnt=0;
for(int i=0;i<n;i++)
if(s[i].fi==t[i].fi)
event[cnt++]={s[i].fi,is[i],it[i],0,-1,0};
for(int i=0;i<m;i++) event[cnt++]={p[i].fi,0,0,ip[i],i,0};
build(1,cs,1);
sort(event,event+cnt);
for(int i=0;i<cnt;i++){
if(event[i].id==-1) update(1,cs,1,event[i].l,event[i].r,event[i].x);
else ans[event[i].id]=min(ans[event[i].id],query(1,cs,1,event[i].b)-p[event[i].id].fi);
}
cnt=0;
for(int i=0;i<n;i++)
if(s[i].se==t[i].se)
event[cnt++]={s[i].se,is[i],it[i],0,-1,0};
for(int i=0;i<m;i++) event[cnt++]={p[i].se,0,0,ip[i],i,0};
build(1,cs,1);
sort(event,event+cnt);
for(int i=0;i<cnt;i++){
if(event[i].id==-1) update(1,cs,1,event[i].l,event[i].r,event[i].x);
else ans[event[i].id]=min(ans[event[i].id],query(1,cs,1,event[i].b)-p[event[i].id].se);
}
cnt=0;
for(int i=0;i<n;i++)
if(s[i].se==t[i].se)
event[cnt++]={s[i].fi,1,is[i],0,-1,s[i].se};
for(int i=0;i<m;i++) event[cnt++]={p[i].fi,0,0,ip[i],i};
build(1,cs,1);
sort(event,event+cnt);
for(int i=0;i<cnt;i++){
if(event[i].id==-1) update(1,cs,1,event[i].l,event[i].r,event[i].y);
else ans[event[i].id]=min(ans[event[i].id],query(1,cs,1,event[i].b)-p[event[i].id].se);
}
cnt=0;
for(int i=0;i<n;i++)
if(s[i].fi==t[i].fi)
event[cnt++]={t[i].se,it[i],cs,0,-1,t[i].fi};
for(int i=0;i<m;i++) event[cnt++]={p[i].se,0,0,ip[i],i};
build(1,cs,1);
sort(event,event+cnt);
for(int i=0;i<cnt;i++){
if(event[i].id==-1) update(1,cs,1,event[i].l,event[i].r,event[i].y);
else ans[event[i].id]=min(ans[event[i].id],query(1,cs,1,event[i].b)-p[event[i].id].fi);
}
for(int i=0;i<m;i++) printf("%d\n",ans[i]);
}
return 0;
}