「CQOI2015」 任务查询系统 - 差分+主席树

题目描述

最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分。超级计算机中的任务用三元组(Si,Ei,Pi)(S_i,E_i,P_i)(Si,Ei,Pi)描述,(Si,Ei,Pi)(S_i,E_i,P_i)(Si,Ei,Pi)表示任务从第SiS_iSi秒开始,在第EiE_iEi秒后结束(第SiS_iSi秒和EiE_iEi秒任务也在运行),其优先级为PiP_iPi。同一时间可能有多个任务同时执行,它们的优先级可能相同,也可能不同。调度系统会经常向查询系统询问,第XiX_iXi秒正在运行的任务中,优先级最小的KiK_iKi个任务(即将任务按照优先级从小到大排序后取前KiK_iKi个)的优先级之和是多少。特别的,如果KiK_iKi大于第XiX_iXi秒正在运行的任务总数,则直接回答第XiX_iXi秒正在运行的任务优先级之和。上述所有参数均为整数,时间的范围在111nnn之间(包含111nnn)。

输入格式

输入文件第一行包含两个空格分开的正整数mmmnnn,分别表示任务总数和时间范围。接下来mmm行,每行包含三个空格分开的正整数SiS_iSiEiE_iEiPi(Si≤Ei)Pi(S_i\le Ei)Pi(SiEi),描述一个任务。接下来nnn行,每行包含四个空格分开的整数XiX_iXiAiA_iAiBiB_iBiCiC_iCi,描述一次查询。查询的参数KiK_iKi需要由公式 Ki=1+(Ai∗Pre+Bi)mod  CiK_i=1+(A_i*Pre+B_i) \mod C_iKi=1+(AiPre+Bi)modCi计算得到。其中PrePrePre表示上一次查询的结果,对于第一次查询,Pre=1Pre=1Pre=1

输出格式

输出共nnn行,每行一个整数,表示查询结果。

数据范围

对于100%的数据,1≤m,n,Si,Ei,Ci≤1051\le m,n,S_i,E_i,C_i\le 10^51m,n,Si,Ei,Ci1050≤Ai,Bi≤1050\le A_i,B_i\le 10^50Ai,Bi1051≤Pi≤1071\le P_i\le10^71Pi107XiX_iXi111nnn的一个排列。

分析

看到第kkk小,第一反应联想到主席树,再一看题,发现有统一区间修改与统一单点询问,可以联想到差分。把这两个东西结合起来,就能过掉此题。

在一开始的修改中,对于Si,Ei,PiS_i,E_i,P_iSi,Ei,Pi,在SiS_iSiPiP_iPi位置加1,Ei+1E_i+1Ei+1PiP_iPi位置减1,然后再跑一遍前缀和,将当前节点的线段树与前一棵线段树以主席树的方式合并,即若当前节点没有值,则让当前节点指向前一棵树的对应节点,以节省空间。当然,本题要求求和,故加个和的值累加即可。若当前查询的KKK大于当前区间的总个数,直接返回当前区间的和。本题值域比较大,故要对PiP_iPi进行离散化。当递归到底部,即l==rl==rl==r时,不能直接返回当前的和,要返回当前位置对应的数的KKK个和。

若本题询问与查询混在一起,应该要用树状数组套主席树,动态维护主席树的前缀和。

代码

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <queue>
using namespace std;
const int N=400004,LogN=20;
const int INF=0x3f3f3f3f;
#define int long long
#define L(x) (nd[x].l)
#define R(x) (nd[x].r)
struct Node {
	int sum,cnt;
	int l,r;
}nd[N*LogN];
int rt[N],nn,tot;
int n,m,b[N],lastans;
int si[N],ei[N],pi[N];
void Add(int &p,int l,int r,int x,int v) {//更改 
	if (!p) p=++tot;
	nd[p].cnt+=v;
	nd[p].sum+=b[x]*v;
	if (l==r) return;
	int mid=(l+r)>>1;
	if (x<=mid) Add(nd[p].l,l,mid,x,v);
	else Add(nd[p].r,mid+1,r,x,v);
}
void Merge(int x,int&y,int l,int r) {//合并x->y 
	if (!y) {//没有对应节点,直接指向前一个节点 
		y=x;
		return;
	}
	nd[y].cnt+=nd[x].cnt;//修改值 
	nd[y].sum+=nd[x].sum;
	if (l==r) return;
	int mid=(l+r)>>1;
	if (nd[x].l)Merge(nd[x].l,nd[y].l,l,mid);//递归左右区间 
	if (nd[x].r) Merge(nd[x].r,nd[y].r,mid+1,r); 
}
int Ask(int p,int l,int r,int k) {
	if (nd[p].cnt<=k) return nd[p].sum;//询问小于个数 
	if (l==r) return (nd[p].sum/nd[p].cnt)*k;//k个当前数的和 
	int cnt=nd[L(p)].cnt;
	int mid=(l+r)>>1;
	if (cnt>=k) return Ask(L(p),l,mid,k);//左区间个数>=k,递归左区间 
	else return nd[L(p)].sum+Ask(R(p),mid+1,r,k-cnt);//否则递归右区间,求前k-cnt小的数的和+左区间的和 
}
signed main() {
	scanf("%lld%lld",&m,&n);
	for (int i=1;i<=m;i++) {
		scanf("%lld%lld%lld",&si[i],&ei[i],&pi[i]);
		b[i]=pi[i];
	}
	sort(b+1,b+m+1);
	nn=unique(b+1,b+m+1)-b-1;
	for (int i=1;i<=m;i++) {
		pi[i]=lower_bound(b+1,b+nn+1,pi[i])-b;//离散化 
		Add(rt[si[i]],1,nn,pi[i],1);//差分 
		Add(rt[ei[i]+1],1,nn,pi[i],-1);
	}
	for (int i=1;i<=n;i++)
		Merge(rt[i-1],rt[i],1,nn);
	lastans=1;
	for (int i=1;i<=n;i++) {
		int x,a,b,c,k;
		scanf("%lld%lld%lld%lld",&x,&a,&b,&c);
		k=1+(a*lastans+b)%c;
		lastans=Ask(rt[x],1,nn,k);
		printf("%lld\n",lastans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值