bzoj4137[FJOI2015]火星商店问题

分析:

首先,一看到求val xor x最大,我们就应该想到可持久化Trie,(详见P4585),这样,当我们询问 L,RL,RL,R 之间的最大异或值时,就直接套用可持久化Trie就行了,这样空间这一维就可以处理了。如果全是特殊商品的话,这个问题就解决了,但还有时间这一维啊,怎么办?(把出题人吊起来打一顿,让他改数据

时间怎么办?

首先,每个火星人能够买的商品的进货时间应该是在一段时间区间内(对普通商品而言),那么只有在这个区间内的商品才能被该火星人买,即对该次询问做贡献,而如果我们把商品看成一个,那么某次询问就是对一段区间上所有的点做一次询问,那么,我们会发现:

这个可以用线段树维护

如果我们在线处理的话,就会重复运算很多次,就可能会 $TLE$ ,其实我也没试过,于是,我们就要引出这题正解了:

线段树分治

所谓线段树分治,就是把某一段区间用线段树的方法分成很多段子区间,再把修改和询问一起放在线段树上,然后再统一一起处理一遍,这样做的好处在于,可以省掉许多重复计算,提高效率。

好了,那么对于这题而言,只要用线段树的方法处理区间询问和单点修改就行了。也就是对于当前区间 $[L,R]$先把完全包含它的所有询问用这一区间内的(商品)更新,再把在左/右区间的点和与左/右区间有交集且没有用该区间更新过的用的询问一起往下递归处理,直到没有询问传递下来为止。
具体看代码吧

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<string>
#include<iostream>
#include<queue>
#include<iomanip>
#include<algorithm>
using namespace std;
const int N=100010;

int n,m,day,ans[N];
struct node
{
    int time,val,pos;
}a[N*2];
int totm;
struct que
{
    int l,r,tl,tr,x;
}q[N*2];
int totq,id[N],cnt;
int ch[N<<5][2],root[N],sum[N<<5],totp;
node tmpl[N*2],tmpr[N*2];

bool cmp(const node &a,const node &b)
{
    return a.pos<b.pos;
}

void add(int x,int x1,int val)//可持久化trie
{
    int i,j;
    sum[x]=sum[x1]+1;
    for(i=16;i>=0;i--)
    {
        bool f=(val&(1<<i));
        ch[x][!f]=ch[x1][!f];
        x1=ch[x1][f];
        x=ch[x][f]=++cnt;
        sum[x]=sum[x1]+1;
    }
}

int quer(int x,int x1,int val)//可持久化trie
{
    int ans=0,i;
    for(i=16;i>=0;i--)
    {
        ans<<=1;
        bool f=(val&(1<<i));
        if(ch[x1][!f]>ch[x][!f]) ans++;
        else f=!f;
        x=ch[x][!f];x1=ch[x1][!f];
    }
    return ans;
}

int find(int l,int r,int x)
{
    int ml=l;
    if(a[l].pos>x) return 0;
    while(l<r)
    {
        int mid=l+r>>1;
        if(a[mid+1].pos<=x) l=mid+1;
        else r=mid;
    }
    return l-ml+1;
}

void work(int ml,int mr,int qr)//用可持久化trie更新询问
{
    totp=0;cnt=0;
    int i,j,k;
    for(i=ml;i<=mr;i++)
    {
        totp++;cnt++;
        root[totp]=cnt;
        add(root[totp],root[totp-1],a[i].val);
    }
    for(i=1;i<=qr;i++)
    {
        int l=find(ml,mr,q[id[i]].l-1);
        int r=find(ml,mr,q[id[i]].r);
        ans[id[i]]=max(ans[id[i]],quer(root[l],root[r],q[id[i]].x));
    }
}

void slove(int ml,int mr,int tl,int tr,int tp)
{
    if(ml>mr||tp==0) return;
    int tot=0,i,j;
    for(i=1;i<=tp;i++)
        if(q[id[i]].tl<=tl&&tr<=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(i=ml;i<=mr;i++)
    {
        if(a[i].time<=mid) tmpl[++lt]=a[i];
        else tmpr[++rt]=a[i];
    }
    for(i=1;i<=lt;i++) a[ml+i-1]=tmpl[i];
    for(i=1;i<=rt;i++) a[ml+lt+i-1]=tmpr[i];
    tot=0;
    for(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]);
    }
    slove(ml,ml+lt-1,tl,mid,tot);
    tot=0;
    for(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]);
    }
    slove(ml+lt,mr,mid+1,tr,tot);
}

int main()
{
    int i,j,op,k,d;
    totq=day=0;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
    {
        scanf("%d",&a[i].val);
        a[i].pos=i;a[i].time=0;
    }
    int spt;
    spt=totm=n;
    for(i=1;i<=m;i++)
    {
        scanf("%d",&op);
        if(op==0)
        {
            day++;a[++totm].time=day;
            scanf("%d%d",&a[totm].pos,&a[totm].val);
        }
        else
        {
            totq++;
            id[totq]=totq;
            scanf("%d%d%d%d",&q[totq].l,&q[totq].r,&q[totq].x,&d);
            q[totq].tr=day;q[totq].tl=max(day-d+1,1);
        }
    }
    sort(a+spt+1,a+totm+1,cmp);
    work(1,spt,totq);//特殊商品单独处理
    slove(spt+1,totm,1,day,totq);//线段树分治
    for(i=1;i<=totq;i++) printf("%d\n",ans[i]);
    return 0;
}

 

posted @ 2019-04-27 11:52  ⚡lyh0313⚡  阅读(189)  评论(0编辑  收藏  举报