[APIO2018]新家——线段树

题面

  LOJ#2585

解析

   看到这个题,第一反应就是离线按时间排序,枚举时间节点,用某种数据结构维护当前时间点的信息

  然后我就不知道怎么维护了

  结果是线段树?

  当然维护信息的过程很巧妙,但需要想到二分长度,把询问转化为在区间$[x-mid, x+mid]$内是否存在$k$种商店才可能想到用线段树维护信息

  线段树中每个叶子节点存与这个点具有相同类型的商店的前驱,但有可能多个商店在同一个点,因此就存所有前驱的最小值, 如果这个点没有了商店,那这个值就是$inf$,但也有可能多个相同类型的商店在同一个点,因此存下标小于这个点的前驱,由于有插入商店与删除商店的操作,因此对每个叶子节点开一个$multiset$维护信息,取出最小的元素作为当前叶子节点的值,再向上更新父亲节点即可。而为了在插入一个商店时可以快速确定它的前驱与后继进行修改,每一个商店类型也都需要开一个$multiset$维护这个类型的所有商店的下标。为了防止找不到前驱或后继的情况,在每个类型的商店的$multiset$中都插入$inf$与$-inf$,当然,相应地,就需要在线段树的$inf$处插入前驱$-inf$,总共插入$k$次

  有了线段树维护信息,那么怎么判定当前二分的区间?

  因为我们维护了前驱的下标,判定在区间$[x-mid, x+mid]$内是否存在k种商店, 变成判定在$(x+mid,inf]$中所有叶子节点的最小前驱的最小值是否大于等于$x-mid$,二分+线段树区间查询可以在$O(log^{2}N)$内完成一次查询。其实可以直接放在线段树上进行二分,每一次都是提取出一个后缀区间。设需要查询的下标是$x$,当前二分的节点是$[l, r]$,中点是$mid$,开一个变量$mn$存$(r, inf]$中所有前驱的最小值,一个临时变量$tmp$存$(mid, inf]$中所有前驱的最小值,即$tmp = min(mn, tr[rs].mn)$我们强制让$mid$在$x$的右边,即若$mid < x$,就走右儿子,不更新$mn$;若$mid - x < x - tmp$,也是走右儿子,也不更新$mn$;若$mid - x > x - tmp$,就走左二子,此时把$tmp$的值赋给$mn$。走到叶子节点时,$l - x$就是答案

  最后一点细节,就是关于$inf$的取值问题。$inf$不能取太大,因为这和线段树的树高与大小有关,$inf$开大了,就会浪费空间与时间;而开小了在查询答案时会出错。考虑一种极限情况,当前查询的点在$1e8$, 而所有商店均在$1$,结合二分的判定条件,$inf$至少为$19999999$,那么开$2e8$足矣

 代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 300005, inf = 200000000;

inline int read()
{
    int ret, f=1;
    char c;
    while((c=getchar())&&(c<'0'||c>'9'))if(c=='-')f=-1;
    ret=c-'0';
    while((c=getchar())&&(c>='0'&&c<='9'))ret=(ret<<3)+(ret<<1)+c-'0';
    return ret*f;
}

int n, k, m, cnt, ans[maxn];

int tot;
struct thi{
    int pos, tim, num, typ;
    void init(int x, int y, int z, int u)
    {
        pos = x; tim = y; num = z; typ = u;
    }
}t[maxn*3];

bool cmp(thi x, thi y)
{
    return x.tim != y.tim? x.tim < y.tim: x.typ > y.typ;
}

multiset<int> st[maxn*60], s[maxn];
multiset<int> :: iterator pt, ptt;

int root, ndnum;
struct seg_tree{
    int ls, rs, mn;
}tr[maxn*60];

void update(int x)
{
    tr[x].mn = min(tr[tr[x].ls].mn, tr[tr[x].rs].mn);
}

void Modify(int x, int L, int R, int p, int ad, int de)
{
    if(L == R)
    {
        if(ad)    st[x].insert(ad);
        if(de)    st[x].erase(st[x].find(de));
        tr[x].mn = (st[x].empty()? inf: *st[x].begin());
        return ;    
    }
    int mid = (L + R) >> 1;
    if(p <= mid)
    {
        if(!tr[x].ls)    tr[x].ls = ++ ndnum;
        Modify(tr[x].ls, L, mid, p, ad, de);
    }
    else
    {
        if(!tr[x].rs)    tr[x].rs = ++ ndnum;
        Modify(tr[x].rs, mid + 1, R, p, ad, de);
    }
    update(x);
}

int Query(int x)
{    
    int now = root, l = 0, r = inf, mid, mn = inf, tmp;
    while(l < r)
    {
        mid = (l + r) >> 1;
        tmp = min(mn, tr[tr[now].rs].mn);
        if(mid < x || mid - x < x - tmp)
            now = tr[now].rs, l = mid + 1;
        else
            mn = tmp, now = tr[now].ls, r = mid;
    }
    return l - x;
}

int main()
{
    n = read(); k = read();m = read();
    for(int i = 1; i <= n; ++i)
    {
        int x = read(), ty = read(), a = read(), b = read();
        t[++tot].init(x, a, ty, 1);
        t[++tot].init(x, b, ty, -1);
    }
    for(int i = 1; i <= m; ++i)
    {
        int x = read(), y = read();
        t[++tot].init(x, y, i, 0);    
    }
    sort(t + 1, t + tot + 1, cmp);
    tr[0].mn = inf;
    root = ++ ndnum;
    for(int i = 1; i <= k; ++i)
    {
        s[i].insert(inf);
        s[i].insert(-inf);
        Modify(root, 0, inf, inf, -inf, 0);
    }
    for(int i = 1; i <= tot; ++i)
    {
        if(t[i].typ == 1)
        {
            if(s[t[i].num].size() == 2)    ++ cnt;
            ptt = pt = s[t[i].num].upper_bound(t[i].pos);
            -- ptt;
            if(*ptt != t[i].pos)
            {
                Modify(root, 0, inf, *pt, t[i].pos, *ptt);
                Modify(root, 0, inf, t[i].pos, *ptt, 0);
            }
            s[t[i].num].insert(t[i].pos);
        }
        else if(t[i].typ == -1)
        {
            s[t[i].num].erase(s[t[i].num].find(t[i].pos));    
            ptt = pt = s[t[i].num].lower_bound(t[i].pos);
            if(*pt == t[i].pos)    continue;
            -- ptt;
            Modify(root, 0, inf, *pt, *ptt, t[i].pos);
            Modify(root, 0, inf, t[i].pos, 0, *ptt);
            if(s[t[i].num].size() == 2)    -- cnt;
        }
        else
            ans[t[i].num] = (cnt == k? Query(t[i].pos): -1);
    }
    for(int i = 1; i <= m; ++i)
        printf("%d\n", ans[i]);
    return 0;
}
View Code

 

posted @ 2019-10-12 11:42  Mr_Joker  阅读(132)  评论(0编辑  收藏  举报