zoukankan      html  css  js  c++  java
  • [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
    ", ans[i]);
        return 0;
    }
    View Code
  • 相关阅读:
    C#聊天+五子棋
    分页
    用户自定义控件(.ascx)
    一、Text To Speech
    验证码
    白话学习MVC(三)页面周期二
    二、Speech To Text
    微软云体验营 北京站 ,4月27日免费开营啦!名额有限速速报名!
    Windows 8 页面应用测试(2)
    《Windows 8应用开发权威指南》图书开始在网络上预售
  • 原文地址:https://www.cnblogs.com/Joker-Yza/p/11660602.html
Copyright © 2011-2022 走看看