【BZOJ4137】火星商店問題(FJOI2015)-線段樹分治+可持久化trie

測試地址:火星商店問題
題目大意:n 個商店,每個商店都有一個特殊商品,每個人在任何時間都可以買。第一天可能沒有進貨,有若干次詢問,而之後的每天,都有一次進貨和若干次詢問,每次進貨都是某個商店進了某個編號的貨,每次詢問都是詢問在編號爲lr 的商店中,在d 天內進的貨的編號異或x 的最大值。
做法:本題需要用到線段樹分治+可持久化trie。
對於特殊商品,直接用可持久化trie就可以了。而對於其他的部分,可以很容易看出線段樹套可持久化trie的做法,線段樹對時間排序,一棵可持久化trie內不同棵trie按位置從小到大排序,trie內顯然就是存商品的編號了。但直接這樣套的話,空間一定會爆炸,因此我們考慮把詢問離線,然後模擬在線段樹上分治的過程。
對於每個詢問,會在線段樹上分成O(logn) 個節點,因此我們只需要在處理到某一個節點時,對這個節點表示的時間區間內進行的修改操作建可持久化trie,然後對每個有分配到當前節點的詢問,用O(logn) 的複雜度進行詢問就行了,因此修改和詢問的時間複雜度都是O(nlog2n) 。並且,因爲在任何時刻我們都只建一棵可持久化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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章