【CQOI2015】任务查询系统(主席树+差分)

这是一道好题 可以更深的理解主席树

最初的想法是 一开始 一边加入任务 一边维护时间轴

换句话说 对于每个时间点 我们都想维护一颗权值线段树(这里的权值代表着优先级Pi)相当于对于一个任务 我们要维护Ei-Si棵

然而显然MLE 考虑降维

联想到主席树建树方式是通过前缀和来的

既然是求前缀和 这道题的每个任务相当于区间修改 会不会与差分数组有关呢?

答案是正确的

所以一开始我们想维护的Ei-Si棵权值线段树 变成只维护Ei,Si+1这两棵就行了 Ei进行+1,Si+1进行-1

因此我们按顺序 rebuild这些权值线段树 把他们合并成主席树

然后就可以query了

#include<bits/stdc++.h>
#define int long long
#define N 100005
using namespace std;
int m,n,a[N],num,rt[N*100],tot,lson[N*100],rson[N*100],cnt[N*100],sum[N*100];
struct Task
{
    int from,to,val;
}task[N];
inline void pushup(int now)
{
    sum[now]=sum[lson[now]]+sum[rson[now]];
    cnt[now]=cnt[lson[now]]+cnt[rson[now]];
}
inline void build(int &now,int l,int r,int pos,int del)
{
    if(!now) now=++tot;
    if(l==r)
    {
        cnt[now]+=del;
        sum[now]=cnt[now]*a[l];
        return;
    }
    int m=(l+r)>>1;
    if(pos<=m)  build(lson[now],l,m,pos,del);
    else build(rson[now],m+1,r,pos,del);
    pushup(now);
}
inline int rebuild(int x,int y)
{
    if(!x||!y)  return x+y;
    int now=++tot;
    sum[now]=sum[x]+sum[y];
    cnt[now]=cnt[x]+cnt[y];
    lson[now]=rebuild(lson[x],lson[y]);
    rson[now]=rebuild(rson[x],rson[y]);
    return now;
}
inline int query(int now,int l,int r,int k)
{
    if(l==r)    return a[l]*min(k,cnt[now]);
    int m=(l+r)>>1;
    if(cnt[lson[now]]>=k)   return query(lson[now],l,m,k);
    else return sum[lson[now]]+query(rson[now],m+1,r,k-cnt[lson[now]]);
}
main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL);cout.tie(NULL);
    cin>>m>>n;
    for(int i=1;i<=m;i++)
    {
        cin>>task[i].from>>task[i].to>>task[i].val;
        a[i]=task[i].val;
    }
    sort(a+1,a+1+m);
    ++n;
    num=unique(a+1,a+1+m)-a-1;
    for(int i=1;i<=m;i++)
    {
        task[i].val=lower_bound(a+1,a+1+num,task[i].val)-a;
        build(rt[task[i].from],1,num,task[i].val,1);
        build(rt[task[i].to+1],1,num,task[i].val,-1);
    }
    for(int i=1;i<=n;i++)
    {
        rt[i]=rebuild(rt[i-1],rt[i]);
    }
    int pre=1;
    for(int i=1;i<n;i++)
    {
        int x,a,b,c,k;
        cin>>x>>a>>b>>c;
        k=1+(a*pre+b)%c;
        pre=query(rt[x],1,num,k);
        cout<<pre<<'\n';
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值