洛谷 P4585 [FJOI2015]火星商店問題 解題報告
P4585 [FJOI2015]火星商店問題
題目描述
火星上的一條商業街裡按照商店的編號\(1,2,\dots,n\) ,依次排列著\(n\)個商店。商店裡出售的琳琅滿目的商品中,每種商品都用一個非負整數\(val\)來標價。每個商店每天都有可能進一些新商品,其標價可能與已有商品相同。
火星人在這條商業街購物時,通常會逛這條商業街某一段路上的所有商店,譬如說商店編號在區間\([L,R]\)中的商店,從中挑選\(1\)件自己最喜歡的商品。每個火星人對商品的喜好標準各不相同。通常每個火星人都有一個自己的喜好密碼\(x\)。對每種標價為\(val\)的商品,喜好密碼為\(x\)的火星人對這種商品的喜好程度與\(val\)
對於給定的按時間順序排列的事件,計算每個購物的火星人的在本次購物活動中最喜歡的商品,即輸出\(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