P4585 [FJOI2015]火星商店问题

题目描述

火星上的一条商业街里按照商店的编号\(1,2,\dots,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\),用五个整数\(1,L,R,x,d\),表示一位火星人当日在编号为\(L\)到\(R\)的商店购买\(d\)天内的商品,该火星人的喜好密码为\(x\)。

输入输出格式

输入格式:

第\(1\)行中给出\(2\)个正整数\(n,m\),分别表示商店总数和事件总数。

第\(2\)行中有\(n\)个整数,第\(i\)个整数表示商店i的特殊商品标价。

接下来的\(m\)行,每行表示\(1\)个事件。每天的事件按照先事件\(0\),后事件\(1\)的顺序排列。

输出格式:

将计算出的每个事件\(1\)的\(val \ xor \ x\)的最大值依次输出。

说明

\(n, m \le 100000\)

数据中,价格不大于\(100000\)


看题先写了个线段树套可持久化\(trie\)复习。

然后MLE还莫名WA在几百行了,没找到错误于是开始看线段树分治的正解。

结果我太愚钝了看了好几个小时才想明白...


离线分治的基本思想就是借助各种奇奇怪怪的东西消除掉一个维度或几个维度,使剩下的维度可以更简单的进行维护,线段树分治也是这样。

不同的是,线段树分治常常是把询问按某一维度的信息把询问拆开,并放到线段树的节点上进行操作。

具体来说,在这个题我们把询问按时间拆开放到线段树的节点上,然后遍历线段树的同时划分修改,这时候,我们在考虑某个节点的求解的时候,便不需要考虑在这个节点上的修改关于时间这一维度的顺序了。我们可以简单的对这一个节点的修改按地点建可持久化\(trie\),然后把线段树节点上的询问在\(trie\)上询问一下就可以了。

值得一提的是,这里的修改对询问是独立贡献的,才可以这么做。如果形式是其他的,可能需要其他形式的线段树分治了。


Code:

#include <cstdio>
#include <algorithm>
#include <vector>
const int N=1e5+10;
int max(int x,int y){return x>y?x:y;}
int n,m;
struct chg
{
int tim,p,x;
bool friend operator <(chg n1,chg n2){return n1.p<n2.p;}
}C[N],lC[N],rC[N];
int ccnt,qcnt;
struct qry
{
int id,l,r,L,R,x;
}Q[N];
int siz[N*24],ch[N*24][2],tot,root[N],ans[N];
void Insert(int las,int &now,int x,int dep)
{
siz[now=++tot]=siz[las]+1;
if(dep<0) return;
int bit=x>>dep&1;
ch[now][bit^1]=ch[las][bit^1];
Insert(ch[las][bit],ch[now][bit],x,dep-1);
}
int ask(int las,int now,int x,int dep)
{
if(dep<0) return 0;
if(!siz[now]) return -N;
int bit=x>>dep&1;
if(siz[ch[now][bit^1]]-siz[ch[las][bit^1]])
return ask(ch[las][bit^1],ch[now][bit^1],x,dep-1)+(1<<dep);
else
return ask(ch[las][bit],ch[now][bit],x,dep-1);
}
std::vector <int > q[N<<2];
#define ls id<<1
#define rs id<<1|1
void change(int id,int l,int r,int p)
{
if(Q[p].L>Q[p].R) return;
if(Q[p].L<=l&&Q[p].R>=r) {q[id].push_back(p);return;}
int mid=l+r>>1;
if(Q[p].L<=mid) change(ls,l,mid,p);
if(Q[p].R>mid) change(rs,mid+1,r,p);
}
int s[N],cnt;
void work(int id,int l,int r)
{
cnt=tot=0;
for(int i=l;i<=r;i++)
{
s[++cnt]=C[i].p;
Insert(root[cnt-1],root[cnt],C[i].x,17);
}
for(int i=0;i<q[id].size();i++)
{
int p=q[id][i];
int L=std::upper_bound(s+1,s+1+cnt,Q[p].l-1)-s-1;
int R=std::upper_bound(s+1,s+1+cnt,Q[p].r)-s-1;
ans[p]=max(ans[p],ask(root[L],root[R],Q[p].x,17));
}
}
void seg(int id,int l,int r,int L,int R)//前时间后修改
{
if(L>R) return;
work(id,L,R);
if(l==r) return;
int lp=0,rp=0,mid=l+r>>1;
for(int i=L;i<=R;i++)
{
if(C[i].tim<=mid)
lC[++lp]=C[i];
else
rC[++rp]=C[i];
}
for(int i=L;i<=L+lp-1;i++) C[i]=lC[i+1-L];
for(int i=L+lp;i<=R;i++) C[i]=rC[i+1-L-lp];
seg(ls,l,mid,L,L+lp-1),seg(rs,mid+1,r,L+lp,R);
}
int main()
{
scanf("%d%d",&n,&m);
for(int a,i=1;i<=n;i++) scanf("%d",&a),Insert(root[i-1],root[i],a,17);
for(int op,s,v,l,r,x,d,i=1;i<=m;i++)
{
scanf("%d",&op);
if(op)
{
scanf("%d%d%d%d",&l,&r,&x,&d);++qcnt;
Q[qcnt]={qcnt,l,r,max(1,ccnt-d+1),ccnt,x};
}
else
{
scanf("%d%d",&s,&v);++ccnt;
C[ccnt]={ccnt,s,v};
}
}
for(int i=1;i<=qcnt;i++)
{
ans[i]=ask(root[Q[i].l-1],root[Q[i].r],Q[i].x,17);
change(1,1,ccnt,i);
}
std::sort(C+1,C+1+ccnt);
seg(1,1,ccnt,1,ccnt);
for(int i=1;i<=qcnt;i++) printf("%d\n",ans[i]);
return 0;
}

2018.11.29

04-30 07:06