測試地址:火星商店問題
題目大意:有 個商店,每個商店都有一個特殊商品,每個人在任何時間都可以買。第一天可能沒有進貨,有若干次詢問,而之後的每天,都有一次進貨和若干次詢問,每次進貨都是某個商店進了某個編號的貨,每次詢問都是詢問在編號爲 到 的商店中,在 天內進的貨的編號異或 的最大值。
做法:本題需要用到線段樹分治+可持久化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;
}