题目链接:[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;
}