[BZOJ 3932][CQOI2015]任务查询系统

Link:

BZOJ 3932 传送门

Solution:

一道比较基础的数据结构题吧,然而一开始我想的还是两个$log$……

明显看出我们是要对每一个时刻$x$维护各个权值区间内数的个数及数的和,主席树的经典模型

在一开始建树时可以使用前缀数组差分的方式:

对于任务$(l,r,x)$,修改$<l,x,1>$和$<r+1,x,-1>$之后第$k$点的情况就是其前缀和啦

 

我一开始想的实现方式竟然是两个$log$的带修改主席树……

但实际上由于此题查询时无修改,一开始按照从小到大的顺序依次更新就能直接算出每个点的前缀和

完全没有必要用树状数组套主席树来更新啊……

 

Tip:

1、由于可能要对每个点更新多次,因此每次都以原来的自己为模板新增链

这时要注意$cnt$和$cur$的更新顺序!

2、到达目标值时要注意不能直接返回该点的$sum$

可能该值有多个且取不完,要返回$k*val$!

Code:

#include <bits/stdc++.h>

using namespace std;
const int MAXN=1e5+10;
typedef long long ll;
struct PrTree{int ls,rs,cnt;ll sum;}seg[MAXN*40];
vector<int> S[MAXN],T[MAXN];ll res=1;
int n,m,a,b,c,x,mx,mn=1<<30,rt[MAXN],dsp[MAXN],tot,cnt;

int idx(int x)
{return lower_bound(dsp+1,dsp+tot+1,x)-dsp;}

void Update(int &cur,int pos,int val,int l,int r)
{//由于在自己上增加,要后对cur赋值 
    seg[++cnt]=seg[cur],cur=cnt;
    seg[cur].cnt+=val;seg[cur].sum+=1ll*val*dsp[pos];
    if(l==r) return;int mid=(l+r)>>1;
    if(pos<=mid) Update(seg[cur].ls,pos,val,l,mid);
    else Update(seg[cur].rs,pos,val,mid+1,r);
}

ll Query(int x,int k,int l,int r)
{//注意,不一定全取啊 
    if(l==r) return 1ll*k*dsp[l];
    int mid=(l+r)>>1,t=seg[seg[x].ls].cnt;
    if(k<=t) return Query(seg[x].ls,k,l,mid);
    else return Query(seg[x].rs,k-t,mid+1,r)+seg[seg[x].ls].sum;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        dsp[i]=c;mn=min(mn,a);mx=max(mx,b+1);
        S[a].push_back(c);T[b+1].push_back(c);
    }
    sort(dsp+1,dsp+n+1);tot=unique(dsp+1,dsp+n+1)-dsp-1;
    
    for(int i=mn;i<=mx;i++)
    {//之后都以自己为模板构造 
        rt[i]=++cnt;seg[rt[i]]=seg[rt[i-1]];
        for(int j=0;j<S[i].size();j++)
            Update(rt[i],idx(S[i][j]),1,1,tot);
        for(int j=0;j<T[i].size();j++)
            Update(rt[i],idx(T[i][j]),-1,1,tot);
    }
    
    while(m--)
    {
        scanf("%d%d%d%d",&x,&a,&b,&c);
        int k=1+(a*res+b)%c;
        if(k>=seg[rt[x]].cnt) printf("%lld\n",res=seg[rt[x]].sum);
        else printf("%lld\n",res=Query(rt[x],k,1,tot));
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/newera/p/9363015.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值