[bzoj3932][主席树]任务查询系统

本文介绍了一种针对超级计算机任务管理系统的查询算法。该系统利用主席树数据结构处理任务的优先级查询,支持快速获取指定时刻运行的任务中优先级最低的若干任务的优先级之和。

Description

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

Input

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

Output

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

Sample Input

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

Sample Output

2

8

11

HINT

样例解释

K1 = (1*1+3)%2+1 = 1

K2 = (1*2+3)%4+1 = 2

K3 = (2*8+4)%3+1 = 3

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

题解

区间修改单点求值我们可以差分一下
那么主席树其实也是可以的嘻嘻嘻
对于l~r这段区间,rt[l]加权 rt[r+1]减去相同的权
最后前缀和起来,单点询问
主席树里面记录两个权,c和sum,c表示这棵子树中有的节点数,sum表示这棵子树里面有的权值和
然后直接找就OK了

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
struct LSnode{int l,r;LL op;}w[210000];
bool cmp(LSnode n1,LSnode n2){return n1.op<n2.op;}
struct node
{
    int lc,rc,c;LL sum;
}tr[5110000];int trlen;
int rt[210000];
void add(int &now,int l,int r,int p,int op,LL del)
{
    if(now==0)now=++trlen;
    tr[now].c+=op;tr[now].sum+=del;
    if(l==r)return ;
    int mid=(l+r)/2;
    if(p<=mid)add(tr[now].lc,l,mid,p,op,del);
    else add(tr[now].rc,mid+1,r,p,op,del);
}
void merge(int &x,int y)
{
    if(x==0){x=y;return ;}
    if(y==0)return ;
    tr[x].c+=tr[y].c;tr[x].sum+=tr[y].sum;
    merge(tr[x].lc,tr[y].lc);
    merge(tr[x].rc,tr[y].rc);
}
int n,m;LL lastans;
LL findKth(int x,int l,int r,int K)
{
    if(K>tr[x].c)return tr[x].sum;
    if(l==r){return (LL)K*(tr[x].sum/tr[x].c);}
    int lc=tr[x].lc,rc=tr[x].rc,mid=(l+r)/2;
    if(K<=tr[lc].c)return findKth(lc,l,mid,K);
    else return findKth(rc,mid+1,r,K-tr[lc].c)+tr[lc].sum;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d%d%lld",&w[i].l,&w[i].r,&w[i].op);
    sort(w+1,w+1+n,cmp);
    int cnt=0;
    for(int i=1;i<=n;i++)
    {
        if(w[i].op==w[i-1].op)add(rt[w[i].l],1,n,cnt,1,w[i].op),add(rt[w[i].r+1],1,n,cnt,-1,-w[i].op);
        else cnt++,add(rt[w[i].l],1,n,cnt,1,w[i].op),add(rt[w[i].r+1],1,n,cnt,-1,-w[i].op);
    }
    for(int i=1;i<=n;i++)merge(rt[i],rt[i-1]);
    lastans=1;
    for(int i=1;i<=m;i++)
    {
        int x,a,b,c;
        scanf("%d%d%d%d",&x,&a,&b,&c);
        int K=1+(a*lastans+b)%c;
        lastans=findKth(rt[x],1,n,K);
        printf("%lld\n",lastans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值