【LG4585】[FJOI2015]火星商店问题

【LG4585】[FJOI2015]火星商店问题

题面

bzoj权限题

洛谷

\(Notice:\)

关于题面的几个比较坑的地方:

  • “一天”不是一个操作,而是有0操作就相当于一天开始了,然后下面的紧跟着的1操作都算这一天的,直到再次出现0操作为止。当然第一个操作可能会是1操作这个时候也算第一天(比如样例……)

  • 0操作是在这个位置添加一个数而不是改成这个数

  • d=0的时候不算这一天

垃圾题面,害我被坑

题解

\(xgzc\)说线段树分治要先开这题

但是我觉得他在放屁 \(\Leftarrow\)我要暴政!!!

因为上面那道题在网上冷门得多

可以发现 ,考虑时间,我们可以将每个人的询问看作一个区间,将添加看作一个单点

之后我们可以直接将每个询问的区间按时间打在线段树上

其中每个线段树上放一个\(vector\)存一下有那些询问

然后时间的影响就可以忽略了

直接用可持久化\(trie\)维护一下值就可以了

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring> 
#include <cmath> 
#include <algorithm>
#include <vector> 
using namespace std; 
inline int gi() {
    register int data = 0, w = 1;
    register char ch = 0;
    while (!isdigit(ch) && ch != '-') ch = getchar(); 
    if (ch == '-') w = -1, ch = getchar(); 
    while (isdigit(ch)) data = 10 * data + ch - '0', ch = getchar(); 
    return w * data; 
}
const int MAX_N = 1e5 + 5; 
namespace Trie { 
    struct Trie { int ch[2], v; } t[MAX_N << 5]; int tot, rt[MAX_N]; 
    void insert(int &o, int p, int v, int i) { 
        o = ++tot, t[o] = t[p], ++t[o].v; 
        if (i == -1) return ; 
        bool c = (v >> i) & 1; 
        insert(t[o].ch[c], t[p].ch[c], v, i - 1); 
    } 
    int query(int v, int u, int w, int i) { 
        if (i == -1) return 0; 
        bool c = (w >> i) & 1; 
        int res = t[t[u].ch[c ^ 1]].v - t[t[v].ch[c ^ 1]].v; 
        if (res) return query(t[v].ch[c ^ 1], t[u].ch[c ^ 1], w, i - 1) + (1 << i); 
        else return query(t[v].ch[c], t[u].ch[c], w, i - 1); 
    } 
} 
struct Mdy { int s, v, t; } q[MAX_N], lq[MAX_N], rq[MAX_N];
bool operator < (const Mdy &l, const Mdy &r) { return l.s < r.s; } 
struct Qry { int l, r, tl, tr, v; } p[MAX_N];
int m_cnt, q_cnt; 
int N, M, ans[MAX_N]; 
#define lson (o << 1) 
#define rson (o << 1 | 1) 
vector<int> vec[MAX_N << 2]; 
void modify(int o, int l, int r, int ql, int qr, int pos) { 
    if (ql > qr) return ; 
    if (ql <= l && r <= qr) return (void)vec[o].push_back(pos); 
    int mid = (l + r) >> 1; 
    if (ql <= mid) modify(lson, l, mid, ql, qr, pos); 
    if (qr > mid) modify(rson, mid + 1, r, ql, qr, pos); 
} 
int stk[MAX_N], top; 
void calc(int o, int l, int r) { 
    top = Trie::tot = 0; 
    for (int i = l; i <= r; i++) { 
        stk[++top] = q[i].s; 
        Trie::insert(Trie::rt[top], Trie::rt[top - 1], q[i].v, 17); 
    } 
    for (vector<int>::iterator ite = vec[o].begin(); ite != vec[o].end(); ++ite) { 
        int k = *ite; 
        int l = upper_bound(&stk[1], &stk[top + 1], p[k].l - 1) - stk - 1; 
        int r = upper_bound(&stk[1], &stk[top + 1], p[k].r) - stk - 1; 
        ans[k] = max(ans[k], Trie::query(Trie::rt[l], Trie::rt[r], p[k].v, 17)); 
    } 
} 
void Div(int o, int lval, int rval, int st, int ed) { 
    if (st > ed) return ; 
    calc(o, st, ed);
    if (lval == rval) return ; 
    int mid = (lval + rval) >> 1, lt = 0, rt = 0; 
    for (int i = st; i <= ed; i++) 
        if (q[i].t <= mid) lq[++lt] = q[i]; 
        else rq[++rt] = q[i]; 
    for (int i = 1; i <= lt; i++) q[st + i - 1] = lq[i]; 
    for (int i = 1; i <= rt; i++) q[st + lt + i - 1] = rq[i]; 
    Div(lson, lval, mid, st, st + lt - 1); 
    Div(rson, mid + 1, rval, st + lt, ed); 
} 

int main () { 
    N = gi(), M = gi(); 
    for (int i = 1; i <= N; i++) Trie::insert(Trie::rt[i], Trie::rt[i - 1], gi(), 17); 
    for (int i = 1; i <= M; i++) {
        int op = gi(); 
        if (op == 0) { int s = gi(), v = gi(); q[++m_cnt] = (Mdy){s, v, m_cnt}; } 
        else {
            int l = gi(), r = gi(), v = gi(), d = gi(); 
            p[++q_cnt] = (Qry){l, r, max(1, m_cnt - d + 1), m_cnt, v};
            ans[q_cnt] = Trie::query(Trie::rt[l - 1], Trie::rt[r], v, 17); 
        } 
    } 
    for (int i = 1; i <= q_cnt; i++) modify(1, 1, m_cnt, p[i].tl, p[i].tr, i); 
    sort(&q[1], &q[m_cnt + 1]); 
    Div(1, 1, m_cnt, 1, m_cnt); 
    for (int i = 1; i <= q_cnt; i++) printf("%d\n", ans[i]); 
    return 0; 
} 

转载于:https://www.cnblogs.com/heyujun/p/10325261.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值