任务查询系统「主席树+差分」
题目描述
思路分析
求 \(k\) 小,主席树经典问题,稍微有一点不同的就是这题要求的是前 \(k\) 个的总和,但其实并没什么区别,只需要在线段树中分别记录个数和 \(sum\) 就好了。
因为 \(p_i\leq10^7\),所以需要离散化。
对于每一个任务,由于生效时间是一个区间,所以可以差分,即对于每个任务对应的区间,拆成两个点,在 \(l\) 处 \(+1\),在 \(r+1\) 处 \(-1\)。
另外由于将每个区间拆成了两个点,所以空间需要比一般的主席树再多开一倍。
\(Code\)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#define N 100010
#define R register
#define ll long long
using namespace std;
inline int read(){
int x = 0,f = 1;
char ch = getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int cnt,ls[N<<6],rs[N<<6],b[N],a[N],n,m,root[N<<6];
ll ans = 1;
struct Segment_tree{
ll sum;
int cnt;
}tr[N<<6];
vector<int>s[N],t[N];
void modify(int &rt,int l,int r,int pre,int aim,int val){
rt = ++cnt;
tr[rt] = tr[pre];
ls[rt] = ls[pre],rs[rt] = rs[pre];
tr[rt].cnt += val,tr[rt].sum += 1ll*val*b[aim];
if(l==r)return;
int mid = (l+r)>>1;
if(aim<=mid)modify(ls[rt],l,mid,ls[pre],aim,val);
else modify(rs[rt],mid+1,r,rs[pre],aim,val);
}
ll query(int rt,int l,int r,int k){
if(l==r)return 1ll*b[l]*k;
int mid = (l+r)>>1;
int tmp = tr[ls[rt]].cnt;
if(k<=tmp)return query(ls[rt],l,mid,k);
else return query(rs[rt],mid+1,r,k-tmp)+tr[ls[rt]].sum;
}
int main(){
m = read(),n = read();
for(R int i = 1;i <= m;i++){
int x = read(),y = read();
b[i] = a[i] = read();
s[x].push_back(i),t[y+1].push_back(i);
}
sort(b+1,b+1+m);
int tot = unique(b+1,b+1+m)-(b+1);
for(R int i = 1;i <= n;i++){
root[i] = root[i-1];
for(R int j = 0;j < s[i].size();j++){
int tmp = lower_bound(b+1,b+1+tot,a[s[i][j]])-b;
modify(root[i],1,tot,root[i],tmp,1);
}
for(R int j = 0;j < t[i].size();j++){
int tmp = lower_bound(b+1,b+1+tot,a[t[i][j]])-b;
modify(root[i],1,tot,root[i],tmp,-1);
}
}
for(R int i = 1;i <= n;i++){
int x = read(),ai = read(),bi = read(),ci = read();
int k = 1+(1ll*ai*ans+bi)%ci;
if(k>tr[root[x]].cnt)ans = tr[root[x]].sum;
else ans = query(root[x],1,tot,k);
printf("%lld\n",ans);
}
return 0;
}