P3168 [CQOI2015]任务查询系统「题解」-可持久化线段树

「P3168 [CQOI2015]任务查询系统」--传送门

大致题意

给你(m)条信息,即一个优先级为(p_i)的任务,处理时间为(l_i~r_i)
给你(n)条询问,每条询问:在(x)时刻优先级前(K)小的任务(p)的总和为多少。
(1<=n,m<=10^5,1<=p_i<=10^7)

题解

(n,m)这样的范围,大概是(O(nlogn))吧,并且有前(k)小,而且是对于一个序列的操作,同时他问的是(x)时刻,所以我们要知道每一个时刻树的版本,为了节省内存,要用可持久化线段树去维护值域
我们可以将每一时刻作为一个版本,每一个版本的树中包含这一时刻正在进行的任务及其优先级
我们可以用差分的思想,若一个优先级为(p_i)的任务在([l,r])区间内执行,我们就可以在(l)版本时加上数(p_i),(l~r)版本之间不动它,使其依次向后承接/转移,到了(r+1)版本将其删除即可。
对于一个版本,我们用主席树维护值域区间([1,10^7]),也就是优先级的值域,从而在求前(k)小的时候能够二分确定位置。可以离散化,但我懒得写了。
我们对于树上节点不仅要维护一个(size)从而确定前(k)小的范围,还要维护一个(val)去求和,每次向该节点及其子节点(如果不是叶子节点)加数的时候,(val)都要加上该数的值,删除的话就减去,我们这样就可以用该节点的(val)来表示出该节点所代表范围中所有加过数的总和,从而更方便的求出前(k)小的和。
查询也跟平时的主席树一样,只不过是个求和而已。
注意细节,访问到叶子节点时,该叶子节点可能size>1,但不选他就不够k个,选他就超过k个,我们要用val/size*k去选出k个。
注意判断:除数不为0。

#include <cstdio>
#include <vector>
#define int long long
using namespace std;
const int maxn=100000+5;
struct Node{
	int lc,rc;
	int siz;
	int val;
};
Node t[maxn*25*2];
int n,m;
int pre=1;
vector<int> a[maxn],b[maxn];//鬼也不知道每个时间点有几次插入,避免开n^2数组,还是vector吧
int root[maxn];
int tot;
void Insert(int &rt,int l,int r,int p,int v){
	int pos=++tot;
	t[pos]=t[rt];
	rt=pos;
	t[rt].siz+=v;
	t[rt].val+=v*p;
	if(l==r) return;
	int mid=(l+r)>>1;
	if(p<=mid) Insert(t[rt].lc,l,mid,p,v);
	else Insert(t[rt].rc,mid+1,r,p,v);
}
int Q(int rt,int l,int r,int k){
	if(l==r&&t[rt].siz!=0) return t[rt].val/t[rt].siz*k;// 注意除数为0特判
	else if(l==r) return 0;
	int mid=(l+r)>>1;
	if(t[t[rt].lc].siz>=k) return Q(t[rt].lc,l,mid,k);
	else return t[t[rt].lc].val+Q(t[rt].rc,mid+1,r,k-t[t[rt].lc].siz);
}
signed main(){
//	freopen("1.in","r",stdin);
	scanf("%lld%lld",&m,&n);
	for(int i=1,x,y,z;i<=m;++i){
		scanf("%lld%lld%lld",&x,&y,&z);
		a[x].push_back(z);
		b[y+1].push_back(z);
	}
	for(int i=1;i<=n;++i){//n个时刻,分别为n个版本
		root[i]=root[i-1];
		for(int j=0;j<a[i].size();++j) Insert(root[i],1,1e7,a[i][j],1);//在这里统一insert是为了维护各版本之间的连续性
		for(int j=0;j<b[i].size();++j) Insert(root[i],1,1e7,b[i][j],-1);
	}
	for(int i=1,x,a,b,c;i<=n;++i){
		scanf("%lld%lld%lld%lld",&x,&a,&b,&c);
		pre=Q(root[x],1,1e7,1+(a*pre+b)%c);
		printf("%lld
",pre);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/Lour688/p/13366906.html