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

题目描述

火星上的一条商业街里按照商店的编号1,2 ,…,n ,依次排列着n个商店。商店里出售的琳琅满目的商品中,每种商品都用一个非负整数val来标价。每个商店每天都有可能进一些新商品,其标价可能与已有商品相同。

火星人在这条商业街购物时,通常会逛这条商业街某一段路上的所有商店,譬如说商店编号在区间[L,R]中的商店,从中挑选1件自己最喜欢的商品。每个火星人对商品的喜好标准各不相同。通常每个火星人都有一个自己的喜好密码x。对每种标价为val的商品,喜好密码为x的火星人对这种商品的喜好程度与val异或x的值成正比。也就是说,val xor x的值越大,他就越喜欢该商品。每个火星人的购物卡在所有商店中只能购买最近d天内(含当天)进货的商品。另外,每个商店都有一种特殊商品不受进货日期限制,每位火星人在任何时刻都可以选择该特殊商品。每个商店中每种商品都能保证供应,不存在商品缺货的问题。

对于给定的按时间顺序排列的事件,计算每个购物的火星人的在本次购物活动中最喜欢的商品,即输出val xor x的最大值。这里所说的按时间顺序排列的事件是指以下2种事件:

事件0,用三个整数0,s,v,表示编号为s的商店在当日新进一种标价为v 的商品。

事件1,用5个整数1,L,R,x,d,表示一位火星人当日在编号为L到R的商店购买d天内的商品,该火星人的喜好密码为x。

题解

考虑如果没有时间限制,我们可以选取的区间为连续一段,那样就是简单的按位贪心,可以用可持久化trie维护。

现在有了时间限制,我们考虑分治,每个询问所覆盖的都是连续一段区间,而且修改之间是独立的,我们可以把每个询问拆成log个挂在按照时间建立的线段树上,然后按时间在线段树上分治。

我们可以每次都重构trie,复杂度是对的。

代码

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#define N 210002
using namespace std;
vector<int>vec[N<<2];
int len=18,tot,inv[20],T[N],ans[N],n,m,tim,top;
inline int rd(){
    int x=0;char c=getchar();bool f=0;
    while(!isdigit(c)){if(c==‘-‘)f=1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f?-x:x;
}
struct TRIE{
    int ch[N*22][2],size[N*22];
    inline void ins(int &now,int pre,int x,int deep){
        now=++tot;ch[now][0]=ch[pre][0];ch[now][1]=ch[pre][1];size[now]=size[pre]+1;
        if(deep<0)return;
        if(x&(1<<deep))ins(ch[now][1],ch[pre][1],x,deep-1);
        else ins(ch[now][0],ch[pre][0],x,deep-1);
    }
    inline int query(int now,int pre,int x,int deep){
        if(deep<0)return 0;
        int o=(x&(1<<deep))!=0,num=size[ch[now][!o]]-size[ch[pre][!o]];
        if(num)return inv[deep]+query(ch[now][!o],ch[pre][!o],x,deep-1);
        else return query(ch[now][o],ch[pre][o],x,deep-1);
    }
}tr;
struct node{
    int pos,val,tim;
    bool operator <(const node &b)const{return pos<b.pos;}
}co[N],q1[N],q2[N];
struct nod{int l,r,x,st,en;}q[N];
void calc(int cnt,int l,int r){
    int top=0;tot=0;
    for(int i=l;i<=r;++i){
        ++top;
        tr.ins(T[top],T[top-1],co[i].val,len);
    }
    for(int i=0;i<vec[cnt].size();++i){
        int id=vec[cnt][i];node x;
        x.pos=q[id].l-1;
        int L=upper_bound(co+l,co+r+1,x)-co-l;//!!!!
        x.pos=q[id].r;
        int R=upper_bound(co+l,co+r+1,x)-co-l;
        ans[id]=max(ans[id],tr.query(T[R],T[L],q[id].x,len));
    }
}
void upd(int cnt,int l,int r,int L,int R,int x){
    if(L>R)return;
    if(l>=L&&r<=R){vec[cnt].push_back(x);return;}
    int mid=(l+r)>>1;
    if(mid>=L)upd(cnt<<1,l,mid,L,R,x);
    if(mid<R)upd(cnt<<1|1,mid+1,r,L,R,x);
}
void solve(int cnt,int l,int r,int L,int R){
    if(l>r||L>R)return;
    int mid=(l+r)>>1;
    calc(cnt,L,R);
    int o=0,p=0;
    for(int i=L;i<=R;++i){
        if(co[i].tim<=mid)q1[++o]=co[i];
        else q2[++p]=co[i];
    }
    for(int i=1;i<=o;++i)co[L+i-1]=q1[i];
    for(int i=1;i<=p;++i)co[L+o+i-1]=q2[i];
    if(l!=r)solve(cnt<<1,l,mid,L,L+o-1);
    solve(cnt<<1|1,mid+1,r,L+o,R);
}
int main(){
    n=rd();m=rd();int l,r,x,d,v,opt;
    for(int i=1;i<=n;++i)x=rd(),tr.ins(T[i],T[i-1],x,len);
    inv[0]=1;
    for(int i=1;i<=len;++i)inv[i]=inv[i-1]<<1;
    for(int i=1;i<=m;++i){
        opt=rd();
        if(opt){
            l=rd();r=rd();x=rd();d=rd();
            q[++top]=nod{l,r,x,max(1,tim-d+1),tim};
            ans[top]=tr.query(T[r],T[l-1],x,len);
        }
        else{
            tim++;x=rd();v=rd();co[tim]=node{x,v,tim};
        }
    }
    sort(co+1,co+tim+1);
    for(int i=1;i<=top;++i)upd(1,1,tim,q[i].st,q[i].en,i);
    solve(1,1,tim,1,tim);
    for(int i=1;i<=top;++i)printf("%d\n",ans[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/ZH-comld/p/10146883.html

时间: 2024-10-09 11:52:19

[FJOI2015]火星商店问题(分治+可持久化)的相关文章

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

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4137 关于可持久化01trie树:https://www.cnblogs.com/LadyLex/p/7281110.html 看了看它的两道例题,就没写. 特殊商品可以直接用可持久化trie做. 其他部分用线段树分治.修改是单点的,询问是区间,原来想的是把询问区间定位后有 mlogn 个,在线段树的每个叶子上贡献一番:结果TLE了,因为若是在叶子处贡献,一个询问就要做 r-l+1 次.

【题解】FJOI2015火星商店问题

好几天之前做的题目了,一直想写一下博客也没腾出时间来,今天赶紧把坑给填上呼呼呼~ 这道题首先如果只考虑每个商店中没有时间限制的物品时,我们只需要使用一棵可持久化trie树来维护区间内的异或最大值即可,这样我们可以把两部分的问题分离开来. 之后我们再考虑有时间限制与编号限制的情况下,该怎样做?无脑做法线段树套trie,直接在对应的区间trie树上贪心,如果该条边的最后更新时间在允许的范围内,说明可以走这条边.虽然这样也可以卡过去(主要在于卡空间),但我们来介绍一种更加妙妙的线段树分治做法.其实我感

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

(勿看,仅作笔记) bzoj权限题... https://www.luogu.org/problemnew/show/P4585 对于特殊商品,直接可持久化trie处理一下即可 剩下的,想了一段时间cdq,但是没想出来...应该是不行的 事实上,如果询问的不是最大值,而是一些满足[l,r]的答案等于[1,r]的答案-[1,l-1]的答案的东西,那么的确可以每个询问拆成两个直接cdq... 但是这题就不能..不过可以线段树分治,这是基于[l,r]的答案可以被分成多个线段树上区间(这些区间的并等于[

luogu P4585 [FJOI2015]火星商店问题

luogu 异或最大值显然可以01trie贪心选取 然后涉及到时间区间内元素贡献,可以把trie可持久化 还涉及区间内集合贡献,那么我们搞个线段树,把操作放到对应节点到根的链上,把询问放到对应区间的log个节点上,然后对着每个线段树节点计算贡献,算完后清空trie,空间\(O(nlogn)\),时间两个\(log\) 还有些和时间无关的物品,单独处理即可 #include<bits/stdc++.h> #define LL long long #define uLL unsigned long

bzoj4137 [FJOI2015]火星商店问题

比较容易想到的做法是线段树套字典树,修改操作时在字典树上经过的节点维护一个最近被访问过的时间,这样询问操作只经过满足时间条件的节点,时间复杂度O(NlogN^2)但是因为线段树每个节点都要套个字典树,这样的话空间是不够的,不过由于可以离线处理,我们可以先把每个修改和询问操作所访问的线段树节点保存下来,然后一个个节点去做,这样的话空间复杂度就可以保证了. 代码 1 #include<cstdio> 2 #include<set> 3 #include<vector> 4

Codeforces 620F Xors on Segments 回滚莫队 + 字典树 || 离心询问分治 + 可持久化字典树

Xors on Segments 转换一下变成询问区间选两个数异或的最大值, 要注意的是一个数作为左端点要-1, 所以在回滚莫队的时候用两棵字典树维护. 这个题居然n ^ 2 也能过...  其实用分治 + 可持久化字典树可以做到n * log(n) * log(n), 懒得写了... #include<bits/stdc++.h> #define LL long long #define LD long double #define ull unsigned long long #defin

4763: 雪辉[点分治+可持久化分块]

4763: 雪辉 Time Limit: 39 Sec  Memory Limit: 666 MBSubmit: 85  Solved: 51[Submit][Status][Discuss] Description 上次立下的NOIP退役Flag没有成功 这次就立一个WC狗牌的Flag 三周目的由乃被钦定成为了卡密,她立刻赶去二周目的世界寻找雪辉 但是按照设定,两个平行世界是没法互相影响的,也就是原则上由乃是没法去二周目世界的 这时候Deus又跳出来说,其实设定是作者骗你的,只要爱的力量足够强

线段树分治

2014徐寅展论文<线段树在一类分治问题上的应用>读后感. 线段树分治 线段树分治其实就是有撤销操作的时间分治. 题目让你维护一些信息,每次可以询问,可以执行一种操作,也可以将之前的某个这种操作撤回. 操作容易维护,但撤回操作不容易维护. 需要将操作,询问都离线下来.将时间轴画出来,那么每个操作只在时间轴上的一个区间内生效. 用线段树给这个区间打上这个操作的标记,维护信息. TJOI2018 数学计算 小豆现在有一个数x,初始值为1. 小豆有Q次操作,操作有两种类型: m: x = x * m

YCB 的暑期计划

前言 YCB现在很弱(TAT) 暑假有一个月,赶快狂补一下. 大概的计划如下: 首先前期会以数据结构为主,毕竟代码能力太弱,涉及内容:线段树分治.二进制分组.KD-Tree. 等数据结构做到没有智商的时候加入一波数论,内容为 杜教筛.min_25筛. 然后中途小清新一下,做一些 组合博弈与构造题. 接着继续练代码能力,顺便学一些神奇的暴力:启发式合并.dsu on tree . 然后图论也忘的差不多了,就回过头去学点新东西,大概会有spfa判负环.0/1分数规划.差分约束. 估计这个时候也没有什