zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    设计模式之美学习-设计原则-面向对象基本概念(一)
    redis-分布式锁-设计与使用
    linux常用命令记录(一)
    redis-布隆过滤器使用
    jdk源码阅读-ConcurrentLinkedQueue(一)
    支付宝支付接口-app支付沙箱环境
    RocketMQ-安装
    支付宝支付接口-运行支付宝demo
    elasticsearch-文档-父子文档(十一)
    RTMPdump 源代码分析 1: main()函数
  • 原文地址:https://www.cnblogs.com/Lour688/p/13366906.html
Copyright © 2011-2022 走看看