题解FJOI2015火星商店问题

Posted twilight-sx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解FJOI2015火星商店问题相关的知识,希望对你有一定的参考价值。

  好几天之前做的题目了,一直想写一下博客也没腾出时间来,今天赶紧把坑给填上呼呼呼~

  这道题首先如果只考虑每个商店中没有时间限制的物品时,我们只需要使用一棵可持久化trie树来维护区间内的异或最大值即可,这样我们可以把两部分的问题分离开来。

  之后我们再考虑有时间限制与编号限制的情况下,该怎样做?无脑做法线段树套trie,直接在对应的区间trie树上贪心,如果该条边的最后更新时间在允许的范围内,说明可以走这条边。虽然这样也可以卡过去(主要在于卡空间),但我们来介绍一种更加妙妙的线段树分治做法。其实我感觉在这题的体现上就是离线版树套树?机房大佬貌似不认可,但我确实是这样觉得哒。

  我们考虑在线椴树上的一次区间查询,实际上是将查询的区间分成了log个已经处理好的小区间,分别查询之后再合并达到最终的答案。那么我们可以模拟这个在线段树上分裂区间的操作:

  1.如果当前处理的区间(l,r)完全包含于询问区间,我们将这个询问的区间在这一层进行处理。(这样的询问均不再下传,跳过后续步骤)

  2.如果询问的区间有涉及(l,mid)的范围,递归左区间处理。

  3.如果询问的区间有涉及(mid+1,r)的范围,递归右区间处理。

  (本题分治的区间表示时间的范围,可持久化trie负责处理编号的限制)。

  这样的复杂度是多少?我们可以分处理trie树与查询的两个方面来分析。处理trie树时,我们插入一个值是log的复杂度,而包含一个修改的总分治区间数也是log个,所以是(O(nlog^{2}n))的复杂度。查询也是log的复杂度,一个查询最多被分别查询 log 次。所以复杂度依然是 (O(log^{2}n)) 的。

#include <bits/stdc++.h>
using namespace std;
#define maxn 1000000
int n, m, sz, qt, nt, tot, cnt, timer;
int ans[maxn], sum[maxn * 5], id[maxn], q[maxn];
int bit[30], ch[maxn * 5][2], num[maxn], root[maxn];

int read()
{
    int x = 0, k = 1;
    char c; c = getchar();
    while(c < 0 || c > 9) { if(c == -) k = -1; c = getchar(); }
    while(c >= 0 && c <= 9) x = x * 10 + c - 0, c = getchar();
    return x * k;
}

struct ques
{
    int l, r, s, t, x;
}Q[maxn];

struct modify
{
    int id, val, t;
    friend bool operator <(const modify& a, const modify& b)
    { return a.id < b.id; }
}P[maxn], ql[maxn], qr[maxn];

void Ins(int now, int last, int x, int val)
{
    if(x < 0) return;
    bool t = (bit[x] & val); 
    ch[now][!t] = ch[last][!t]; ch[now][t] = ++ sz;
    sum[ch[now][t]] = sum[ch[last][t]] + 1;
    Ins(ch[now][t], ch[last][t], x - 1, val);
}

int Query(int l, int r, int x, int val)
{
    if(l > r || x < 0) return 0;
    int ans = 0; bool t = val & bit[x];
    if(sum[ch[r][!t]] - sum[ch[l][!t]])
        ans = (bit[x] + Query(ch[l][!t], ch[r][!t], x - 1, val));
    else ans = Query(ch[l][t], ch[r][t], x - 1, val);
    return ans;
}

int Lower(int v)
{
    int l = 1, r = nt, ret = 0;
    while(l <= r)
    {
        int mid = (l + r) >> 1;
        if(num[mid] <= v) ret = mid, l = mid + 1; 
        else r = mid - 1;
    }
    return ret;
}

void Work(int l, int r)
{
    sz = 0, nt = 0;
    for(int i = l; i <= r; i ++)
    {
        nt ++; root[nt] = ++ sz;
        Ins(root[nt], root[nt - 1], 17, P[i].val);
        num[nt] = P[i].id;
    }
    for(int i = 1; i <= qt; i ++)
    {
        int l = Lower(Q[q[i]].l - 1), r = Lower(Q[q[i]].r);
        ans[q[i]] = max(ans[q[i]], Query(root[l], root[r], 17, Q[q[i]].x));
    }
}

void Divide(int l, int r, int L, int R, int pres)
{
    if(l > r || !pres) return;
    int mid = (L + R) >> 1; qt = 0;
    for(int i = 1; i <= pres; i ++)
        if(Q[id[i]].s <= L && R <= Q[id[i]].t) q[++ qt] = id[i]; 
    Work(l, r); if(L == R) return; 
    int ll = 0, rr = 0;
    for(int i = l; i <= r; i ++)
    {
        if(P[i].t <= mid) ql[++ ll] = P[i];
        else qr[++ rr] = P[i];
    }
    for(int i = 1; i <= ll; i ++) P[i + l - 1] = ql[i];
    for(int i = 1; i <= rr; i ++) P[i + l + ll - 1] = qr[i]; 
    
    
    int ind = 0;
    for(int i = 1; i <= pres; i ++)
    {
        if(Q[id[i]].s <= L && R <= Q[id[i]].t) continue;
        if(Q[id[i]].s <= mid) swap(id[i], id[++ ind]);
    }
    Divide(l, l + ll - 1, L, mid, ind);
    ind = 0;
    for(int i = 1; i <= pres; i ++)
    {
        if(Q[id[i]].s <= L && R <= Q[id[i]].t) continue;
        if(Q[id[i]].t > mid) swap(id[i], id[++ ind]);
    }
    Divide(l + ll, r, mid + 1, R, ind);
}

int main()
{
    bit[0] = 1; for(int i = 1; i <= 20; i ++) bit[i] = bit[i - 1] << 1;
    n = read(), m = read();
    for(int i = 1; i <= n; i ++) root[i] = ++ sz, Ins(root[i], root[i - 1], 17, read());
    for(int i = 1; i <= m; i ++)
    {
        int opt = read();
        if(!opt)
        {
            ++ timer; P[++ tot].id = read();
            P[tot].val = read(); P[tot].t = timer;
        }
        else 
        {
            Q[++ cnt].l = read(); Q[cnt].r = read();
            Q[cnt].x = read(); int d = read();
            Q[cnt].s = max(timer - d, 0) + 1; Q[cnt].t = timer; 
            ans[cnt] = Query(root[Q[cnt].l - 1], root[Q[cnt].r], 17, Q[cnt].x);
        }
    }
    sort(P + 1, P + 1 + tot);
    for(int i = 1; i <= cnt; i ++) id[i] = i;
    Divide(1, tot, 1, timer, cnt);
    for(int i = 1; i <= cnt; i ++) printf("%d
", ans[i]);
    return 0;
}

 

以上是关于题解FJOI2015火星商店问题的主要内容,如果未能解决你的问题,请参考以下文章

[FJOI2015]火星商店问题(分治+可持久化)

[FJOI2015]火星商店问题

bzoj4137[FJOI2015]火星商店问题

洛谷 P4585 [FJOI2015]火星商店问题

luogu P4585 [FJOI2015]火星商店问题

bzoj 4137 [FJOI2015]火星商店问题——线段树分治+可持久化01trie树