【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;
}
posted @ 2018-09-11 16:30  Maxwei_wzj  阅读(214)  评论(0编辑  收藏  举报