可持久化trie+线段树分治题 火星商店

89 篇文章 0 订阅
72 篇文章 0 订阅

链接:https://www.luogu.org/problemnew/show/P4585

做法:首先观察到每个人能买的商品是一段区间,考虑将这个区间拆成log段存在线段树节点上(开vector),这样就可以考虑对线段树每个节点所代表的商品区间建一棵可持久化trie,然后更新它这个节点上的所有询问,这样做其实算是一种整体二分,复杂度n*log^2

(做这题第一发wa了,调了1个小时发现空间开小了mmp,原来trie里插入一个x位的数是要开x+1个点的...平时这么开没关系是因为trie里很多节点是共用的,而可持久化trie是严格开满的就gg了......)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+100,lim=1<<17;

template<class T>
void rd(T &x)
{
    char c=getchar();x=0;bool f=0;
    while(!isdigit(c))f|=(c=='-'),c=getchar();
    while(isdigit(c))x=x*10+c-48,c=getchar();
    if(f)x=-x;
}
void Mx(int &x,int y)
{x=max(x,y);}

struct Trie{
    int nxt[20*N][2],sz[20*N],tot,edit[N];
    void clear(){tot=0,nxt[0][0]=nxt[0][1]=0,sz[0]=0,edit[0]=0;}
    int ins(int bf,int x,int mo)
    {
        int nw=++tot;
        sz[nw]=sz[bf]+1;
        if(!mo)return nw;
        nxt[nw][0]=nxt[bf][0],nxt[nw][1]=nxt[bf][1];
        if(x&mo)nxt[nw][1]=ins(nxt[bf][1],x,mo>>1);
        else nxt[nw][0]=ins(nxt[bf][0],x,mo>>1);
        return nw;
    }
    int qry(int fr,int to,int x,int mo)
    {
        if(!mo)return 0;
        int op=(x&mo)?0:1;
        if(sz[nxt[to][op]]-sz[nxt[fr][op]]>=1)return mo+qry(nxt[fr][op],nxt[to][op],x,mo>>1);
        else return qry(nxt[fr][op^1],nxt[to][op^1],x,mo>>1);
    }
    int ask(int fr,int to,int x)
    {return qry(edit[fr],edit[to],x,lim);}
    void insert(int id,int x)
    {edit[id]=ins(edit[id],x,lim);}
}trie;
struct pp{
    int l,r,L,R,x;
    pp(){};
    pp(int l,int r,int L,int R,int x):l(l),r(r),L(L),R(R),x(x){};
}qry[N];int qnum=0;
struct th{
    int tim,wh,val;
    th(){};
    th(int tim,int wh,int val):tim(tim),wh(wh),val(val){};
}buy[N],tax1[N],tax2[N];
int n,m,tim=0,ans[N],hav[N],hnum;
vector<int>seg[N<<2];


bool cmp(th x,th y)
{return x.wh<y.wh;}


void seg_ins(int l,int r,int k,int L,int R,int val)
{
    if(L>R)return;
    if(L<=l&&r<=R)seg[k].push_back(val);
    else
    {
        int mid=(l+r)>>1;
        if(L<=mid)seg_ins(l,mid,k<<1,L,R,val);
        if(R>mid)seg_ins(mid+1,r,k<<1|1,L,R,val);
    }
}

void sol(int l,int r,int L,int R,int k)
{
    if(L>R)return;
    int nw=0;
    trie.clear(),hnum=0;
    for(int i=L;i<=R;i++)
    {
        ++nw,hav[++hnum]=buy[i].wh,trie.edit[nw]=trie.edit[nw-1];
        trie.insert(nw,buy[i].val);
    }
    for(int i=0,lps,rps;i<seg[k].size();i++)
    {
        nw=seg[k][i];
        lps=upper_bound(hav+1,hav+hnum+1,qry[nw].l-1)-hav-1;
        rps=upper_bound(hav+1,hav+hnum+1,qry[nw].r)-hav-1;
        Mx(ans[nw],trie.ask(lps,rps,qry[nw].x));
    }
    if(l==r)return;
    int tnum1=0,tnum2=0,mid=(l+r)>>1;
    for(int i=L;i<=R;i++)
    {
        if(buy[i].tim<=mid)tax1[++tnum1]=buy[i];
        else tax2[++tnum2]=buy[i];
    }
    for(int i=1;i<=tnum1+tnum2;i++)
    {
        if(i<=tnum1)buy[L+i-1]=tax1[i];
        else buy[L+i-1]=tax2[i-tnum1];
    }
    sol(l,mid,L,L+tnum1-1,k<<1);
    sol(mid+1,r,L+tnum1,R,k<<1|1);
}

int main()
{
    rd(n),rd(m);
    trie.clear();
    for(int i=1,tmp;i<=n;i++)
        rd(tmp),trie.edit[i]=trie.edit[i-1],trie.insert(i,tmp);
    int op,x,y,d,l,r;
    for(int i=1;i<=m;i++)
    {
        rd(op);
        if(op)
        {
            rd(l),rd(r),rd(x),rd(d);
            qry[++qnum]=pp(l,r,max(1,tim-d+1),tim,x);
            ans[qnum]=trie.ask(l-1,r,x);
        }
        else
        {
            rd(x),rd(y),++tim;
            buy[tim]=th(tim,x,y);
        }
    }
    for(int i=1;i<=qnum;i++)seg_ins(1,tim,1,qry[i].L,qry[i].R,i);
    sort(buy+1,buy+tim+1,cmp);
    sol(1,tim,1,tim,1);
    for(int i=1;i<=qnum;i++)
        printf("%d\n",ans[i]);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值