【BZOJ4137】火星商店问题(FJOI2015)-线段树分治+可持久化trie
测试地址:火星商店问题
题目大意:有个商店,每个商店都有一个特殊商品,每个人在任何时间都可以买。第一天可能没有进货,有若干次询问,而之后的每天,都有一次进货和若干次询问,每次进货都是某个商店进了某个编号的货,每次询问都是询问在编号为到的商店中,在天内进的货的编号异或的最大值。
做法:本题需要用到线段树分治+可持久化trie。
对于特殊商品,直接用可持久化trie就可以了。而对于其他的部分,可以很容易看出线段树套可持久化trie的做法,线段树对时间排序,一棵可持久化trie内不同棵trie按位置从小到大排序,trie内显然就是存商品的编号了。但直接这样套的话,空间一定会爆炸,因此我们考虑把询问离线,然后模拟在线段树上分治的过程。
对于每个询问,会在线段树上分成个节点,因此我们只需要在处理到某一个节点时,对这个节点表示的时间区间内进行的修改操作建可持久化trie,然后对每个有分配到当前节点的询问,用的复杂度进行询问就行了,因此修改和询问的时间复杂度都是。并且,因为在任何时刻我们都只建一棵可持久化trie,空间问题就迎刃而解了。于是我们就解决了这一题。
(这一道题说是“线段树分治”让我非常疑惑,线段树本身是序列分治过程的一种表示,因此在线段树上的任何问题其实都可算作分治,这道题主要特殊在从树套树通过对询问离线变换成一维分治+一维数据结构的思路,但鉴于网上诸多大佬都把这称作线段树分治,我也就这么叫吧)
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
int n,m,day,rt[100010]={0},totp,totmd,totq;
int ch[2000010][2]={0},sum[2000010]={0};
int ans[100010],id[100010];
struct Modify {int tim,pos,x;} md[200010],tmpl[100010],tmpr[100010];
struct Query {int l,r,x,tl,tr;} q[100010];
void add(int v,int last,int x)
{
sum[v]=sum[last]+1;
for(int i=16;i>=0;i--)
{
bool f=(x&(1<<i));
ch[v][f]=++totp;
ch[v][!f]=ch[last][!f];
v=ch[v][f];
last=ch[last][f];
sum[v]=sum[last]+1;
}
}
int query(int v,int last,int x)
{
int ans=0;
for(int i=16;i>=0;i--)
{
ans<<=1;
bool f=(x&(1<<i));
f=!f;
if (sum[ch[v][f]]-sum[ch[last][f]]>0) ans++;
else f=!f;
v=ch[v][f];
last=ch[last][f];
}
return ans;
}
int lower(int ml,int l,int r,int x)
{
if (md[l].pos>x) return 0;
while(l<r)
{
int mid=(l+r)>>1;
if (md[mid+1].pos<=x) l=mid+1;
else r=mid;
}
return l-ml+1;
}
void work(int ml,int mr,int qr)
{
totp=0;
int tottim=0;
for(int i=ml;i<=mr;i++)
{
rt[++tottim]=++totp;
add(rt[tottim],rt[tottim-1],md[i].x);
}
for(int i=1;i<=qr;i++)
{
int L=lower(ml,ml,mr,q[id[i]].l-1);
int R=lower(ml,ml,mr,q[id[i]].r);
ans[id[i]]=max(ans[id[i]],query(rt[R],rt[L],q[id[i]].x));
}
}
void solve(int ml,int mr,int tl,int tr,int tp)
{
if (ml>mr||!tp) return;
int tot=0;
for(int i=1;i<=tp;i++)
if (q[id[i]].tl<=tl&&tr<=q[id[i]].tr&&q[id[i]].tl<=q[id[i]].tr)
swap(id[i],id[++tot]);
work(ml,mr,tot);
if (tl==tr) return;
int mid=(tl+tr)>>1,lt=0,rt=0;
for(int i=ml;i<=mr;i++)
{
if (md[i].tim<=mid) tmpl[++lt]=md[i];
else tmpr[++rt]=md[i];
}
for(int i=1;i<=lt;i++) md[ml+i-1]=tmpl[i];
for(int i=1;i<=rt;i++) md[ml+lt+i-1]=tmpr[i];
tot=0;
for(int i=1;i<=tp;i++)
{
if (q[id[i]].tl<=tl&&tr<=q[id[i]].tr) continue;
if (q[id[i]].tl<=mid) swap(id[i],id[++tot]);
}
solve(ml,ml+lt-1,tl,mid,tot);
tot=0;
for(int i=1;i<=tp;i++)
{
if (q[id[i]].tl<=tl&&tr<=q[id[i]].tr) continue;
if (q[id[i]].tr>mid) swap(id[i],id[++tot]);
}
solve(ml+lt,mr,mid+1,tr,tot);
}
bool cmp(Modify a,Modify b)
{
return a.pos<b.pos;
}
int main()
{
scanf("%d%d",&n,&m);
day=0;
totmd=totq=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&md[++totmd].x);
md[totmd].tim=0;
md[totmd].pos=i;
}
int st=totmd;
for(int i=1;i<=m;i++)
{
int op;
scanf("%d",&op);
if (!op)
{
day++;
md[++totmd].tim=day;
scanf("%d%d",&md[totmd].pos,&md[totmd].x);
}
else
{
++totq;
id[totq]=totq;
scanf("%d%d%d%d",&q[totq].l,&q[totq].r,&q[totq].x,&q[totq].tl);
q[totq].tl=max(day-q[totq].tl+1,1);
q[totq].tr=day;
}
}
sort(md+1,md+st+1,cmp);
sort(md+st+1,md+totmd+1,cmp);
work(1,st,totq);
solve(st+1,totmd,1,day,totq);
for(int i=1;i<=totq;i++)
printf("%d\n",ans[i]);
return 0;
}