P4632 [APIO2018] New Home 新家 题解
题目描述
\(n\) 个点,给定每个点的坐标,颜色,出现时间和消失时间。
\(q\) 次询问,给定询问的时刻和坐标,求对不同颜色, \(\big (\) 该时刻出现、该颜色的点到给定坐标距离最小值 \(\big)\) 的最大值,如果某种颜色不存在则输出 \(-1\) 。
数据范围
- \(1\le n,q\le 3\cdot 10^5\) , $1\le $ 时间,坐标 \(\le 10^8\) 。
时间限制 \(\texttt{5s}\) ,空间限制 \(\texttt{1GB}\) 。
分析
扫描时间轴,动态维护数轴上点的集合,需要支持加点和删点。
询问先二分答案,转化为判断某个区间内是否包含所有颜色的点。
接下来是个重要的 trick :给每个点记录前面颜色相同的最近点位置 \(pre_x\) 。
钦定每种颜色的第一个点产生贡献,那么区间颜色数等于 \(x\in [l,r],pre_x\lt l\) 的点数。
放在二维平面上,单点修改,矩形求和,树套树维护,算上外层的二分答案,时间复杂度 \(O(q\log^3n)\) 。
三只 \(\log\) 肯定过不去,但我们并不需要知道具体颜色数,只需判断是否每种颜色都出现。
假如每种颜色都出现,那么对 \([r+1,n]\) 中的点 \(x\) , \(pre_x\ge l\) 。
但这并不是充分条件,用 multiset 记录每种颜色最后一个点的位置,显然 multiset 中的最小元素 \(\ge l\) 。
容易发现,上述 \(2\) 个条件同时成立是区间 \([l,r]\) 符合要求的充要条件。
实现细节:
点坐标需要离散化,但不能直接在离散化后的数组上二分,需要对原数轴二分然后把左右端点映射到离散化后的数组上。
为维护
pre数组,我们需要给每种颜色开multiset。添加哨兵0可以用于判无解和规避奇怪的边界情况。注意这里开的是
multiset而非set,因为可能有多个颜色相同的点同时出现在同一位置,此时只需更新multiset而无需在线段树上操作;同理如果删除时这个位置有多个点,只需要从multiset中删掉一个即可。对于离散化后的每个位置,同样需要用
multiset存储不同颜色的pre值,加入哨兵inf可以规避奇怪的边界情况。
时间复杂度 \(\mathcal O(n\log n+q\log n\log V)\) ,由于大量使用 STL 所以常数很大。
#include<bits/stdc++.h>
#define fi first
#define se second
#define mp make_pair
#define pii pair<int,int>
using namespace std;
const int maxn=9e5+5,inf=1e8+5;
int m,n,q,cnt,tmp;
int p[maxn],col[maxn],st[maxn],ed[maxn];
int loc[maxn],tim[maxn];
int c[maxn],t[maxn],res[maxn];
multiset<int> cur,s[maxn],pos[maxn];
vector<pii> v1[maxn],v2[maxn],vec[maxn];
struct node
{
int l,r,mn;
}f[4*maxn];
int read()
{
int q=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') q=10*q+ch-'0',ch=getchar();
return q;
}
void pushup(int p)
{
f[p].mn=min(f[2*p].mn,f[2*p+1].mn);
}
void build(int p,int l,int r)
{
f[p].l=l,f[p].r=r,f[p].mn=inf;
if(l==r) return ;
int mid=(l+r)/2;
build(2*p,l,mid);
build(2*p+1,mid+1,r);
}
void modify(int p,int pos,int val)
{
if(f[p].l==f[p].r)
{
f[p].mn=val;
return ;
}
int mid=(f[p].l+f[p].r)/2;
if(pos<=mid) modify(2*p,pos,val);
else modify(2*p+1,pos,val);
pushup(p);
}
int query(int p,int l,int r)
{
if(l<=f[p].l&&f[p].r<=r) return f[p].mn;
if(l>f[p].r||r<f[p].l) return inf;
return min(query(2*p,l,r),query(2*p+1,l,r));
}
#define s s[col]
void add(int p,int col)
{
cur.erase(cur.find(*--s.end()));
bool flg=s.find(p)!=s.end();
s.insert(p);
cur.insert(*--s.end());
if(flg) return ;
int pre=*--s.lower_bound(p);
pos[p].insert(pre);
modify(1,p,*pos[p].begin());
if(p==*--s.end()) return ;
int nxt=*s.upper_bound(p);
pos[nxt].erase(pos[nxt].find(pre));
pos[nxt].insert(p);
modify(1,nxt,*pos[nxt].begin());
}
void del(int p,int col)
{
cur.erase(cur.find(*--s.end()));
s.erase(s.find(p));
bool flg=s.find(p)!=s.end();
cur.insert(*--s.end());
if(flg) return ;
int pre=*--s.lower_bound(p);
pos[p].erase(pos[p].find(pre));
modify(1,p,*pos[p].begin());
if(p>=*--s.end()) return ;
int nxt=*s.upper_bound(p);
pos[nxt].erase(pos[nxt].find(p));
pos[nxt].insert(pre);
modify(1,nxt,*pos[nxt].begin());
}
#undef s
bool check(int l,int r)
{
l=lower_bound(c+1,c+cnt+1,l)-c;
r=upper_bound(c+1,c+cnt+1,r)-c-1;
return l<=r&&*cur.begin()>=l&&query(1,r+1,cnt)>=l;
}
int main()
{
n=read(),m=read(),q=read();
for(int i=1;i<=n;i++)
{
p[i]=read(),col[i]=read(),st[i]=read(),ed[i]=read();
c[++cnt]=p[i],t[++tmp]=st[i],t[++tmp]=ed[i];
}
for(int i=1;i<=q;i++)
{
loc[i]=read(),tim[i]=read();
res[i]=-1,t[++tmp]=tim[i];
}
sort(c+1,c+cnt+1);
cnt=unique(c+1,c+cnt+1)-c-1;
sort(t+1,t+tmp+1);
tmp=unique(t+1,t+tmp+1)-t-1;
for(int i=1;i<=n;i++)
{
p[i]=lower_bound(c+1,c+cnt+1,p[i])-c;
st[i]=lower_bound(t+1,t+tmp+1,st[i])-t;
ed[i]=lower_bound(t+1,t+tmp+1,ed[i])-t;
v1[st[i]].push_back(mp(p[i],col[i]));
v2[ed[i]].push_back(mp(p[i],col[i]));
}
for(int i=1;i<=q;i++)
{
tim[i]=lower_bound(t+1,t+tmp+1,tim[i])-t;
vec[tim[i]].push_back(mp(loc[i],i));
}
for(int i=1;i<=m;i++) cur.insert(0),s[i].insert(0);
build(1,1,cnt);
for(int i=1;i<=cnt;i++) pos[i].insert(inf);
for(int i=1;i<=tmp;i++)
{
for(auto u:v1[i]) add(u.fi,u.se);
for(auto u:vec[i])
{
if(*cur.begin()==0) continue;
int l=-1,r=inf;
while(r-l>1)
{
int mid=(l+r)/2;
if(check(u.fi-mid,u.fi+mid)) r=mid;
else l=mid;
}
res[u.se]=r;
}
for(auto u:v2[i]) del(u.fi,u.se);
}
for(int i=1;i<=q;i++) printf("%d\n",res[i]);
return 0;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/16344652.html
浙公网安备 33010602011771号