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;
    }
    
  • 相关阅读:
    1012 The Best Rank (25 分)(排序)
    1011. World Cup Betting (20)(查找元素)
    1009 Product of Polynomials (25 分)(模拟)
    1008 Elevator (20 分)(数学问题)
    1006 Sign In and Sign Out (25 分)(查找元素)
    1005 Spell It Right (20 分)(字符串处理)
    Kafka Connect 出现ERROR Failed to flush WorkerSourceTask{id=local-file-source-0}, timed out while wait
    flume、kafka、avro组成的消息系统
    Java23种设计模式总结【转载】
    Java编程 思维导图
  • 原文地址:https://www.cnblogs.com/Lour688/p/13366906.html
Copyright © 2011-2022 走看看