题目链接:[CQOI2015]任务查询系统
因为对于任务来说,对一段区间是有用的,于是我们可以用差分来表示区间,然后主席树维护前缀区间和即可。
然后因为我们是求和,我们同时主席树也要维护区间的数字个数,因为求K小和。
但是有可能当前区间的有a个相同的数字,我们求b个和,然后b<a,然后我们就需要返回 sum*b/a ,不然就会一直只有80分。
AC代码:
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10;
int m,n,s[N],e[N],p[N],rt[N],cnt,len,pre=1;
vector<int> v,g[N];
struct node{int l,r,num,sum;}t[N*40];
void change(int l,int r,int &x,int y,int pos,int k,int flag){
x=++cnt; t[x]=t[y]; t[x].num+=flag; t[x].sum+=k*flag;
if(l==r) return ; int mid=l+r>>1;
if(pos<=mid) change(l,mid,t[x].l,t[y].l,pos,k,flag);
else change(mid+1,r,t[x].r,t[y].r,pos,k,flag);
}
int ask(int l,int r,int x,int k){
if(t[x].num<=k) return t[x].sum;
if(l==r) return t[x].sum*k/t[x].num;//非常重要,防止多个数字
int mid=l+r>>1,s=t[t[x].l].num;
if(s>=k) return ask(l,mid,t[x].l,k);
else return ask(l,mid,t[x].l,s)+ask(mid+1,r,t[x].r,k-s);
}
signed main(){
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
cin>>m>>n;
for(int i=1;i<=m;i++){
cin>>s[i]>>e[i]>>p[i],v.emplace_back(p[i]);
g[s[i]].push_back(i),g[e[i]+1].push_back(-i);
}
sort(v.begin(),v.end()); v.erase(unique(v.begin(),v.end()),v.end());
for(int i=1;i<=m;i++)
p[i]=lower_bound(v.begin(),v.end(),p[i])-v.begin()+1;
len=v.size();
for(int i=1;i<=n;i++){
rt[i]=rt[i-1];
for(int j=0;j<g[i].size();j++){
int val=(g[i][j]>0)?p[g[i][j]]:p[-g[i][j]];
change(1,len,rt[i],rt[i],val,v[val-1],(g[i][j]>0?1:-1));
}
}
while(n--){
static int x,a,b,c,k; cin>>x>>a>>b>>c; k=1+(a*pre%c+b)%c;
cout<<(pre=ask(1,len,rt[x],k))<<endl;
}
return 0;
}