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 次。

要在线段树的每个节点上建可持久化trie,可持久化的顺序就是商店的编号;这样就能对于 mlogn 个区间,先二分找到在可持久化trie的哪一段有贡献,然后贡献给那个询问了。

做出每个节点的可持久化trie,不用trie树合并什么的,只要把当前节点表示的区间的修改排序然后一个一个加进去就行了。一共只会加 nlogn 次。

为了省时间,不用再每个节点的时候给那些修改按商店排序,而可以先按商店排好序,然后像 CDQ 分治那样稳定地把属于左边的给左边、属于右边的给右边。

在线段树的每个节点的时候,虽然可以认为同一个商店的在一棵trie树上,不同商店之间的trie树合并起来,但写的时候不能像 “if( cr==pr ) { ... sm[cr]++; ... }” 这样 “当在同一个商店的时候按普通构建trie树那样赋值” ,因为已经可持久化了, sm[cr]++ 的这个 cr 可能可以通过之前的 rt 走过来,就会出错。

注意空间不是 18*n 而是 19*n ,因为边有 18 层,节点就有 19 层。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define ls Ls[cr]
#define rs Rs[cr]
#define pb push_back
using namespace std;
int rdn()
{
  int ret=0;bool fx=1;char ch=getchar();
  while(ch>‘9‘||ch<‘0‘){if(ch==‘-‘)fx=0;ch=getchar();}
  while(ch>=‘0‘&&ch<=‘9‘)ret=ret*10+ch-‘0‘,ch=getchar();
  return fx?ret:-ret;
}
int Mx(int a,int b){return a>b?a:b;}
const int N=1e5+5,K=17,M=19e5+5;//19 not 18 for -1
int n,m,ct,sp[N],st[N],en[N],ans[N];
int tot,Ls[N<<1],Rs[N<<1],len;
struct Node{
  int x,id,t;
  bool operator< (const Node &b)const
  {return id<b.id;}
}vl[N],tp[N];
struct Ques{int l,r,x;}q[N];
vector<int> vt[N<<1];
namespace Tri{
  int tot,c[M][2],sm[M],rt[N],bin[K+5];
  void Init()
  {bin[0]=1;for(int i=1;i<=K;i++)bin[i]=bin[i-1]<<1;}
  void ins(int x,int cr,int pr)
  {
    rt[cr]=++tot; cr=rt[cr]; pr=rt[pr];
    for(int t=K;t>=0;t--)
      {
    bool d=(x&bin[t]);
    c[cr][d]=++tot; c[cr][!d]=c[pr][!d];
    cr=c[cr][d]; pr=c[pr][d]; sm[cr]=sm[pr]+1;
      }
  }
  int qry(int l,int r,int x)
  {
    l=rt[l-1]; r=rt[r]; int ret=0;
    for(int t=K;t>=0;t--)
      {
    bool d=(x&bin[t]);
    if(sm[c[r][!d]]-sm[c[l][!d]])
      l=c[l][!d], r=c[r][!d], ret|=bin[t];
    else l=c[l][d], r=c[r][d];
      }
    return ret;
  }
}
void build(int l,int r,int cr)
{
  if(l==r)return; int mid=l+r>>1;
  ls=++tot; build(l,mid,ls);
  rs=++tot; build(mid+1,r,rs);
}
void ins(int l,int r,int cr,int L,int R,int k)
{
  if(l>=L&&r<=R){vt[cr].pb(k);return;}
  int mid=l+r>>1;
  if(L<=mid)ins(l,mid,ls,L,R,k);
  if(mid<R)ins(mid+1,r,rs,L,R,k);
}
int fnd(int x,bool fx)
{
  int l=1,r=len,ret=0;
  while(l<=r)
    {
      int mid=l+r>>1;
      if(!fx)
    {if(tp[mid].id>=x)ret=mid,r=mid-1;else l=mid+1;}
      else
    {if(tp[mid].id<=x)ret=mid,l=mid+1;else r=mid-1;}
    }
  return ret;
}
void solve(int l,int r,int cr,int L,int R)
{
  for(int i=L,j=1;i<=R;i++,j++)tp[j]=vl[i];//out of if()
  if(vt[cr].size())
    {
      len=R-L+1;  Tri::tot=0;//
      for(int i=1;i<=len;i++)Tri::ins(tp[i].x,i,i-1);
      for(int i=0,j=vt[cr].size();i<j;i++)
    {
      int bh=vt[cr][i]; Ques k=q[bh];
      int tl=fnd(k.l,0), tr=fnd(k.r,1);//tl,tr not l,r!!!
      if(!tl||!tr||tl>tr)continue;
      ans[bh]=Mx(ans[bh],Tri::qry(tl,tr,k.x));
    }
    }
  if(l==r)return; int mid=l+r>>1,p0=L-1;
  int tl=R-L+1;
  for(int i=1;i<=tl;i++)if(tp[i].t<=mid)vl[++p0]=tp[i];
  int p1=p0;
  for(int i=1;i<=tl;i++)if(tp[i].t>mid)vl[++p0]=tp[i];
  solve(l,mid,ls,L,p1);  solve(mid+1,r,rs,p1+1,R);
}
int main()
{
  n=rdn();int T=rdn();
  for(int i=1;i<=n;i++)sp[i]=rdn();
  for(int i=1,op,d;i<=T;i++)
    {
      op=rdn();
      if(!op)
    {
      m++; vl[m].id=rdn();vl[m].x=rdn();vl[m].t=m;
    }
      else
    {
      ct++; q[ct].l=rdn();q[ct].r=rdn();q[ct].x=rdn();
      d=rdn(); st[ct]=Mx(1,m-d+1);en[ct]=m;
    }
    }
  Tri::Init();
  if(m)//
    {
      tot=1;build(1,m,1);
      for(int i=1;i<=ct;i++)
    if(st[i]<=en[i])ins(1,m,1,st[i],en[i],i);//if
      sort(vl+1,vl+m+1); solve(1,m,1,1,m);
    }
  Tri::tot=0;
  for(int i=1;i<=n;i++)Tri::ins(sp[i],i,i-1);
  for(int i=1;i<=ct;i++)
    {
      ans[i]=Mx(ans[i],Tri::qry(q[i].l,q[i].r,q[i].x));
      printf("%d\n",ans[i]);
    }
  return 0;
}

原文地址:https://www.cnblogs.com/Narh/p/10376459.html

时间: 2024-10-03 14:33:03

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

可持久化0-1Trie树

我跟可持久化数据结构杠上了 \(QwQ\) .三天模拟赛考了两次可持久化数据结构(主席树.可持久化0-1Trie树),woc. 目录: 个人理解 时空复杂度分析 例题及简析 一.个人理解 可持久化0-1Trie树,是一种可以快速查询区间异或信息的高级数据结构. 它的主要思想和主席树相同,即保存每次插入操作的历史版本,来快速查询区间的异或信息. 0-1Trie树和平常写的strTrie树相同,都是维护前缀信息的数据结构.不同点只有一个,就是0-1Trie树是维护一个0-1串.可持久化0-1Trie

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

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

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

【题解】FJOI2015火星商店问题

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

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

题目描述 火星上的一条商业街里按照商店的编号1,2 ,-,n ,依次排列着n个商店.商店里出售的琳琅满目的商品中,每种商品都用一个非负整数val来标价.每个商店每天都有可能进一些新商品,其标价可能与已有商品相同. 火星人在这条商业街购物时,通常会逛这条商业街某一段路上的所有商店,譬如说商店编号在区间[L,R]中的商店,从中挑选1件自己最喜欢的商品.每个火星人对商品的喜好标准各不相同.通常每个火星人都有一个自己的喜好密码x.对每种标价为val的商品,喜好密码为x的火星人对这种商品的喜好程度与val

BZOJ 2741【FOTILE模拟赛】L 分块+可持久化Trie树

题目大意 给出一个序列,求[l, r]中的最大连续xor 和. 强制在线 思路 先把整个序列分成n  √  块,预处理每一块的开头到每个数字的最大连续xor 和.这个我们只需处理出前缀xor 和,之后用可持久化Trie树就可以搞定.这样询问的右边就是整块的了.剩下左边的随便暴力一下就能过了.. CODE #define _CRT_SECURE_NO_WARNINGS #include <cmath> #include <cstdio> #include <cstring>

树分治基础模板以及树的重心(poj1741 tree)

好久没有更新博文了,这里更新一发~~ Give a tree with n vertices,each edge has a length(positive integer less than 1001). Define dist(u,v)=The min distance between node u and v. Give an integer k,for every pair (u,v) of vertices is called valid if and only if dist(u,v

bzoj4137 [FJOI2015]火星商店问题

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

luogu P4585 [FJOI2015]火星商店问题

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