[FJOI2015]火星商店问题 线段树分治

这道题的每个询问都有两个区间,一个是时间区间,一个是商店编号区间。
每个购买也是和时间商店编号有关。
如何让这两个参数联系起来,就需要用到线段树表示时间区间。线段树可以把时间区间分细。对于每一个询问,它都有一个时间区间 [ c n t 1 d + 1 , c n t 1 ] [cnt_1-d+1,cnt_1] (t为当前询问时间,d为询问中向前延长的时间)都要放到线段树上的节点上的vector中,用线段树上vector保存对应时间为 [ l , r ] [l,r] 的询问编号。
解决时,购买先按商店编号排序,以完成询问中第二个区间变量。
线段树分治,参数有now,tl,tr,l,r分别表示线段树端点,购买操作tl,tr的时间是在l,r中,且tl,tr的编号有序。

#include <bits/stdc++.h>
#define ls (now << 1)
#define rs (now << 1 | 1)
#define mid ((l + r) >> 1)
#define pb(x) push_back(x)
#define LL long long
using namespace std;
inline void read(int &x){
    x = 0; int f = 1; char ch = getchar();
    while (!(ch >= '0' && ch <= '9')){if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9'){x = x * 10 + ch - '0'; ch = getchar();}
    x *= f;
}
inline void Max(int &x, int y){if (y > x) x = y;}
inline void Min(int &x, int y){if (y < x) x = y;}
const int MAX = 0x7fffffff;
const int MIN = 0x80000000;
const int INF = 0x3f3f3f3f;
const int N = 1e5 + 10;
int n, m, cnt1, cnt2, ans[N], rt[N];
struct Add{
    int s, v, t;
    Add(int a = 0, int b = 0, int c = 0){s = a, v = b, t = c;}
} q[N], tmp1[N], tmp2[N];
inline bool cmp(Add &a, Add &b){return a.s < b.s;}
struct Ask{
    int l, r, tl, tr, x;
    Ask(int a = 0, int b = 0, int c = 0, int d = 0, int e = 0){l = a, r = b, tl = c, tr = d, x = e;}
} p[N];
struct Trie{
    struct Node{int son[2], w;} T[N << 5];
    int tot;
    inline void Insert(int &x, int y, int w, int now){
        T[x = ++tot] = T[y]; ++T[x].w;
        if (now == -1) return;
        bool c = w & (1 << now);
        Insert(T[x].son[c], T[y].son[c], w, now - 1);
    }
    inline int query(int x, int y, int w, int now){
        if (now == -1) return 0;
        bool c = w & (1 << now);
        int tmp = T[T[y].son[c ^ 1]].w - T[T[x].son[c ^ 1]].w;
        if (tmp) return query(T[x].son[c ^ 1], T[y].son[c ^ 1], w, now - 1) + (1 << now);
        else return query(T[x].son[c], T[y].son[c], w, now - 1);
    }
}trie;
vector<int> ve[N];
struct Seg{
    inline void Modify(int now, int tl, int tr, int x, int l, int r){
        if (tl > tr) return;
        if (l == tl && r == tr){ve[now].pb(x); return;}
        if (tr <= mid) Modify(ls, tl, tr, x, l, mid);
        else if (tl > mid) Modify(rs, tl, tr, x, mid + 1, r);
        else Modify(ls, tl, mid, x, l, mid), Modify(rs, mid + 1, tr, x, mid + 1, r);
    }
    int top, st[N];
    inline int low(int x){
        int l = 1, r = top;
        while (l <= r){
            if (st[mid] <= x) l = mid + 1;
            else r = mid - 1;
        }
        return r;
    }
    inline void calc(int now, int tl, int tr){
        if (!ve[now].size()) return;
        top = trie.tot = 0;
        for (int i = tl; i <= tr; i++){
            st[++top] = q[i].s;
            trie.Insert(rt[top], rt[top - 1], q[i].v, 17);
        }
        for (int i = 0, sz = ve[now].size(); i < sz; i++){
            int k = ve[now][i];
            int l = low(p[k].l - 1), r = low(p[k].r);
            Max(ans[k], trie.query(rt[l], rt[r], p[k].x, 17));
        }
    }
    //divide(线段树上节点编号,[tl,tr]表示被时间[l,r]分成的q数组上的购买时间)
    //相当于是对每一个购买时间进行分治,并用线段树上的节点的vector来表示查询问题
    //线段树作用只是用来保存查询问题,使购买和查询拥有共同的时间段
    inline void divide(int now, int tl, int tr, int l, int r){
        if (tl > tr) return;
        int t1 = 0, t2 = 0;
        calc(now, tl, tr); if (l == r) return;
        for (int i = tl; i <= tr; i++)
            if (q[i].t <= mid) tmp1[++t1] = q[i];
            else tmp2[++t2] = q[i];
        for (int i = 1; i <= t1; i++) q[i + tl - 1] = tmp1[i];
        for (int i = 1; i <= t2; i++) q[i + tl - 1 + t1] = tmp2[i];
        divide(ls, tl, tl + t1 - 1, l, mid);
        divide(rs, tl + t1, tr, mid + 1, r);
    }
}seg;
int main(){
    read(n), read(m);
    for (int i = 1; i <= n; i++){
        int x; read(x);
        trie.Insert(rt[i], rt[i - 1], x, 17);
    }
    for (int i = 1; i <= m; i++){
        int opt; read(opt);
        if (!opt){
            int s, v; read(s), read(v); ++cnt1;
            q[cnt1] = Add(s, v, cnt1);
        }else{
            int l, r, x, d; read(l), read(r), read(x), read(d);
            ans[++cnt2] = trie.query(rt[l - 1], rt[r], x, 17);
            p[cnt2] = Ask(l, r, max(1, cnt1 - d + 1), cnt1, x);
        }
    }
    for (int i = 1; i <= cnt2; i++)
        seg.Modify(1, p[i].tl, p[i].tr, i, 1, cnt1);
    sort(q + 1, q + 1 + cnt1, cmp);
    seg.divide(1, 1, cnt1, 1, cnt1);
    for (int i = 1; i <= cnt2; i++) printf("%d\n", ans[i]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44627639/article/details/88622658