[BZOJ 3932][CQOI2015]任务查询系统
Link:
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; }