[FJOI2015]火星商店问题(线段树分治+可持久化Trie)

重新写一年前抄题解的那题,当时我啥都不会只是Ctrl+C,Ctrl+V写过的题,今天重新写一遍。

题解:

不会线段树分治,还是学一下这东西吧,这是我的第一道线段树分治。

首先对于特殊商品,可以直接可持久化Trie记录答案。首先考虑对每个线段树开一个vector,把询问的时间区间看成一段一段的塞到线段树里,修改实际上是相当于一个后缀。然后把修改按位置排序,然后仿照线段树的形式按时间分治,mid前的修改扔到左边数组里递归,反之扔到右边,这样能够排除这一维的限制。然后空间上用可持久化Trie,前缀和相减即可。最开始的修改,要按照商店顺序排序,这样可以把中间的商店忽略,一个修改挨着一个修改建主席树,复杂度得以保证。

#include<bits/stdc++.h>
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
const int N=1e5+7;
struct guest{int l,r,L,R,x;}p[N];
struct buy{int s,v,t;}q[N],t1[N],t2[N];
int n,m,n1,n2,tot,top,rt[N],ans[N],st[N],ch[N*19][2],sz[N*19];
vector<int>a[N];
bool cmp(buy x,buy y){return x.s<y.s;}
void build(int&x,int u,int S)
{
    x=++tot;
    int now=x;
    for(int i=17;~i;i--)
    {
        bool d=S>>i&1;
        ch[now][d^1]=ch[u][d^1],ch[now][d]=++tot;
        now=ch[now][d],u=ch[u][d],sz[now]=sz[u]+1;
    }
}
int query(int l,int r,int S)
{
    int ret=0;
    for(int i=17;~i;i--)
    {
        bool d=S>>i&1;
        if(sz[ch[r][d^1]]-sz[ch[l][d^1]]>0)l=ch[l][d^1],r=ch[r][d^1],ret+=1<<i;
        else l=ch[l][d],r=ch[r][d];
    }
    return ret;
}
void update(int L,int R,int x,int l,int r,int rt)
{
    if(L>R)return;
    if(L<=l&&r<=R){a[rt].push_back(x);return;}
    int mid=l+r>>1;
    if(L<=mid)update(L,R,x,lson);
    if(R>mid)update(L,R,x,rson);
}
void calc(int x,int L,int R)
{
    top=tot=0;
    for(int i=L;i<=R;i++)st[++top]=q[i].s,build(rt[top],rt[top-1],q[i].v);
    for(int i=0,k,l,r;i<a[x].size();i++)
    {
        k=a[x][i];
        l=upper_bound(st+1,st+top+1,p[k].l-1)-st-1,r=upper_bound(st+1,st+1+top,p[k].r)-st-1;
        ans[k]=max(ans[k],query(rt[l],rt[r],p[k].x));
    }
}
void divide(int l,int r,int rt,int L,int R)
{
    if(L>R)return;
    int mid=l+r>>1,cnt1=0,cnt2=0;
    calc(rt,L,R);
    if(l==r)return;
    for(int i=L;i<=R;i++)if(q[i].t<=mid)t1[++cnt1]=q[i];else t2[++cnt2]=q[i];
    for(int i=1;i<=cnt1;i++)q[i+L-1]=t1[i];
    for(int i=1;i<=cnt2;i++)q[i+L-1+cnt1]=t2[i];
    divide(lson,L,L+cnt1-1);
    divide(rson,L+cnt1,R);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1,x;i<=n;i++)scanf("%d",&x),build(rt[i],rt[i-1],x);
    for(int i=1,op,l,r,x,d;i<=m;i++)
    {
        scanf("%d%d%d",&op,&l,&r);
        if(!op)q[++n1]=(buy){l,r,n1};
        else{
            scanf("%d%d",&x,&d);
            ans[++n2]=query(rt[l-1],rt[r],x);
            p[n2]=(guest){l,r,max(1,n1-d+1),n1,x};
        }
    }
    for(int i=1;i<=n2;i++)update(p[i].L,p[i].R,i,1,n1,1);
    sort(q+1,q+n1+1,cmp);
    divide(1,n1,1,1,n1);
    for(int i=1;i<=n2;i++)printf("%d\n",ans[i]);
}
View Code

 

posted @ 2019-06-17 23:03  hfctf0210  阅读(149)  评论(0编辑  收藏  举报