可持久化线段树 模板 【bzoj3932】任务查询系统


    人的运势就如同一条正弦曲线不断波动起伏。如果你觉的你一直在走下坡路,那只能说明,你还没有到达最低点。        ——Todobe

题目大意:
给m个任务,第i个任务从si秒持续到ei秒,有权值pi。
有n次查询,询问第x秒前k小的权值和。
强制在线。

题目分析:
如果可以离线的话只需要把修改和查询排个序正常权值线段树就好。
但是这是在线的,所以我们就让这个线段树……更持久(持久化)。

我们按照时间顺序修改线段树,在每一个时间点上都建立一个线段树,询问的时候就找到这个时间点的线段树去查询就好啦。

但是空间复杂度O(n^2)是很不友好的,但是可以发现,我们每次单点修改最多都只改logn个线段树节点,那我们每次新建一整颗果实饱满有生机富有活力的线段树就有点浪费了。所以我们可以“嫁接”,如果只修改左儿子的话,那么右儿子就可以用上一个时间点的了。这样每次只需要新建logn个节点,就变成O(nlogn)的了,至此,时空和谐了……

注意事项:
1、这个代码是完全自己写的并改了1天的成果,因为第一次写,不一定具有代表性,此题可过。该代码只给读者作为参考,了解更多更好模板,可以参考其他朋友的博客。
一颗骨骼惊奇的主席树

2、因为权值可能是相同的,所以线段树叶子节点内可能有多个数,所以在访问到叶子节点的时候如果k小于该叶子的size,要返回sum*k/size。

代码如下:

/* persistent_segment_tree by Todobe */
#include<cstdio>
#include<algorithm>
#include<iostream>
#define N 300000
using namespace std;
int n,m,s,e,p,x,l,r;
long long a,b,c,k,pre=1;
struct modi{
    int tim,val;
}mo[N],dis[N];
int rnk[N];
#define num tim
bool cmp1 (modi x,modi y) {return x.tim<y.tim;}
bool cmp2 (modi x,modi y) {return x.val<y.val;}
struct seg{
    seg *ls,*rs;
    int sz,sum;
    seg()
    {
        ls=rs=NULL;
        sz=sum=0;
    }
    void push_up()
    {
        sz=sum=0;
        if(ls) sz+=ls->sz,sum+=ls->sum;
        if(rs) sz+=rs->sz,sum+=rs->sum;
    }
    void change(int l,int r,int k,int v)
    {
        if(l==r)
        {
            sz+=v>0?1:-1;
            sum+=v;
            return;
        }
        int mid=l+r>>1;
        seg *y=new seg();
        if(k<=mid)
        {
            if(ls) *y=*ls;
            ls=y; ls->change(l,mid,k,v);
        }
        else
        {
            if(rs) *y=*rs;
            rs=y; rs->change(mid+1,r,k,v);
        }
        push_up();
        return;
    }
    int query(int k)
    {
        if(k==0) return 0;
        if(sz<=k) return sum;
        if(sz>k && !ls && !rs) return sum*k/sz;
        if(ls)
        {
            if(ls->sz>=k) return ls->query(k);
            else          return ls->sum+rs->query(k-ls->sz);
        }
        else return rs->query(k);
        return 0;
    }
}*root[N];
int query(int x,int k)
{
    int l=0,r=2*m,mid,ans=l;
    while(l<=r)
    {
        mid=l+r>>1;
        if(mo[mid].tim<=x) { ans=mid; l=mid+1; }
        else                 r=mid-1;
    }
    return root[ans]->query(k);
}
int main()
{
    scanf("%d%d",&m,&n);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&s,&e,&p);
        mo[i].tim=s;     mo[i].val=p;
        mo[i+m].tim=e+1; mo[i+m].val=-p;
    }
    sort(mo+1,mo+1+2*m,cmp1);
    for(int i=1;i<=2*m;i++)
    {
        dis[i].num=i;
        dis[i].val=mo[i].val>0?mo[i].val:-mo[i].val;
    }
    sort(dis+1,dis+1+2*m,cmp2);
    dis[0].val=-1;
    for(int i=1;i<=2*m;i++)
    {
        if(dis[i].val==dis[i-1].val)  rnk[dis[i].num]=rnk[dis[i-1].num];
        else                          rnk[dis[i].num]=rnk[dis[i-1].num]+1;
    }

    l=1;r=rnk[dis[2*m].num];
    root[0]=new seg();
    for(int i=1;i<=2*m;i++)
    {
        root[i]=new seg();
        *root[i]=*root[i-1];
        root[i]->change(l,r,rnk[i],mo[i].val);
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d%lld%lld%lld",&x,&a,&b,&c);
        k=1+(a*pre+b)%c;
        pre=query(x,k);
        printf("%lld\n",pre);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值