NKOJ3251 (CQOI2015) 任务查询系统(差分数组,主席树)

问题描述

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

输入格式

        输入文件第一行包含两个空格分开的正整数m和n,分别表示任务总数和时间范围。
        接下来m行,每行包含三个空格分开的正整数Si,Ei和Pi(Si≤Ei),描述一个任务。
        接下来n行,每行包含四个空格分开的整数Xi,Ai,Bi和Ci,描述一次查询。查询的参数Ki需要由公式 Ki = 1 + ( Ai * Pre + Bi ) mod Ci 计算得到。其中Pre表示上一次查询的结果,对于第一次查询,Pre=1。

输出格式

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

样例输入

4 3
1 2 6
2 3 3
1 3 2
3 3 4
3 1 3 2
1 1 3 4
2 2 4 3

样例输出

2
8
11

















提示

样例解释
        K1=(1*1+3)%2+1=1;
        K2=(1*2+3)%4+1=2;
        K3=(2*8+4)%3+1=3。

数据范围
        对于50%的数据,Ai=0。
        对于100%的数据,1≤m,n,Si,Ei,Ci≤100000,0≤Ai,Bi≤100000,1≤Pi≤10000000,Xi为1到n的一个排列。


    求前K小的和,实际上和求第K小一样,过程中累加一下sum就好。

    然后根据时间建立n棵主席树,每个任务会修改Si到Ei这么多数,区间修改,用一点差分的思想相当于是在第Si棵树上加,在第Ei+1棵树上减,最后求个前缀就好。

    具体实现时只需要按照操作的时间排序后不断加入就好。当将时间在Xi前的操作全部加入树上后得到的就是Xi时间的树。

    最后说一下,因为在查询答案时到达底层节点时要取得前K个,所以需要将sum/cnt*k。(亲测乱写能得80分)


附上代码

#include<stdio.h>
#include<iostream>
#include<algorithm>
#define ll long long
#define M 20000000
#define N 300000
using namespace std;
struct node{ll x,y,ny,d;}A[N];
bool cmp(node a,node b)
{return a.x<b.x;}
ll B[N],rt[N],m,n,pre=1;
ll ls[M],rs[M],cnt[M],sum[M],tot;
ll CO(ll p)
{
	ll o=++tot;
	ls[o]=ls[p];rs[o]=rs[p];
	cnt[o]=cnt[p];sum[o]=sum[p];
	return o;
}
ll ADD(ll p,ll l,ll r,ll k,ll sd,ll cd)
{
	ll o=CO(p);
	if(l==r){cnt[o]+=cd;sum[o]+=cd*sd;}
	else
	{
		ll mid=l+r>>1;
		if(k<=mid)ls[o]=ADD(ls[o],l,mid,k,sd,cd);
		else rs[o]=ADD(rs[o],mid+1,r,k,sd,cd);
		cnt[o]=cnt[ls[o]]+cnt[rs[o]];
		sum[o]=sum[ls[o]]+sum[rs[o]];
	}
	return o;
}
ll ASK(ll p,ll l,ll r,ll k)
{
	if(l==r)return sum[p]/cnt[p]*k;
	ll mid=l+r>>1;
	if(cnt[ls[p]]>=k)return ASK(ls[p],l,mid,k);
	return ASK(rs[p],mid+1,r,k-cnt[ls[p]])+sum[ls[p]];
}
int main()
{
	ll i,j=1,k,p,a,b,c;
	scanf("%lld%lld",&m,&n);
	for(i=1;i<=m;i++)
	{
		scanf("%lld%lld%lld",&a,&b,&c);
		B[i]=c;
		A[i].x=a;A[i].y=c;A[i].d=1;
		A[i+m].x=b+1;A[i+m].y=c;A[i+m].d=-1;
	}
	sort(B+1,B+m+1);
	sort(A+1,A+2*m+1,cmp);
	for(i=1;i<=2*m;i++)A[i].ny=lower_bound(B+1,B+m+1,A[i].y)-B;
	for(i=1;i<=n;i++)
	{
		rt[i]=rt[i-1];j--;
		while(A[++j].x==i)rt[i]=ADD(rt[i],1,m,A[j].ny,A[j].y,A[j].d);
	}
	for(i=1;i<=n;i++)
	{
		scanf("%lld%lld%lld%lld",&k,&a,&b,&c);
		p=1+(a*pre+b)%c;
		if(p>cnt[rt[k]])pre=sum[rt[k]];
		else pre=ASK(rt[k],1,m,p);
		printf("%lld\n",pre);
	}
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值