peiwenjun's blog 没有知识的荒原

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;
}

posted on 2022-06-05 19:05  peiwenjun  阅读(12)  评论(0)    收藏  举报

导航