1.31 【可持久化线段树】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
对于100%的数据,1≤m,n,Si,Ei,Ci≤100000,0≤Ai,Bi≤100000,1≤Pi≤10000000,Xi为1到n的一个排列

我的题解

这万恶的任务查询系统要询问任何时刻的前k小优先度之和。显然,任何时刻(可持久化)的前k小优先度之和(线段树)。

我们自然希望有这样一种东西就好了:对于任意的时刻,都有一棵线段树。这棵线段树维护的是当前时刻,任何一个优先度区间[l,r]中所在进行的任务数量的和以及它们优先度的和。然后我们在这棵线段树上找前k小的任务,把他们的优先度之和加起来(线段树的基本作用,求和),就可以了。同时我们还会发现这种线段树是可加减的。

对于每个时间都造一棵线段树显然会空间爆炸。于是我们构造可持久化线段树。

既然是可加减的,那么构造时就和差分数组类似。对于每一个任务,如果它的时间是t1~t2,优先度为f,我们就在原线段树的基础上,构造两棵新线段树。构造一个t1时刻的线段树,只需要在原来线段树的基础上将[f,f]这个区间的任务数+1,优先度之和+f;再构造t2+1时刻的线段树,在原来线段树的基础上将[f,f]这个区间的任务数-1,优先度之和-f。(具体的可持久化线段树写法,我的和大家的好像不太一样,但是应该还好懂)。

然后我们离散一下时间(这里的离散化是我在网上学的,挺巧妙,但是我找不到是哪位大神的博客了)。对于访问的任何一个时刻t,若满足t1<=t< t2,则认为访问的是t1的那棵线段树。当然,实际上还会有t3,t4之类,但只要是对于一段没有任何修改的时间中的任何时刻,都认为和这段时间的开始时刻是等效的。这样询问操作也解决了。

附上代码。
写的是数组线段树。
L[i]表示i节点的左儿子节点编号。
s[i]表示i节点为根的这棵线段树中所有的任务数量。
SS[i]表示优先度之和。
T[i]是第i棵线段树的根,对应的是离散后的每一个时刻。
to[i]就是刚才说的时间离散,对于任何时刻t(t1<=t< t2),to[t]=t1。

#include<cstdio> 
#include<queue>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<iostream>
#include<cstdlib>
#define mid (l+r)/2
using namespace std;
inline int read(){
    char c;int ret=0;
    do{c=getchar();}while(c>'9'||c<'0');
    do{ret=ret*10+c-48;c=getchar();}while(c>='0'&&c<='9');
    return ret;
}
int a,b,c;
long long pre=1;
int T[1000010],to[1000010];
int L[11000000],R[11000000];
long long SS[11000000];
int s[11000000];
int n,m;
struct E{
    int pos,v;
};
E e[1000000];
bool cmp(E xxx,E yyy){
    return xxx.pos<yyy.pos;
}
int cnt=0;
int maxx=0;
int cntt=0;
inline int aabbsss(int x){return x>0?x:-x;}
int update(int pr,int l,int r,int x){
    int ret;
    ret=++cntt;
    L[ret]=L[pr];
    R[ret]=R[pr];
    if(x>0)s[ret]=s[pr]+1;
    else s[ret]=s[pr]-1;
    SS[ret]=SS[pr]+x;
    if(l<r)
    {
        int v=aabbsss(x);
        if(v<=mid) L[ret]=update(L[pr],l,mid,x);
        else R[ret]=update(R[pr],mid+1,r,x);
    }
    return ret;
}
long long query(int u,int l,int r,int k){
    long long ret=0;
    if(s[u]<=k)return SS[u];
    if(l==r)
    {
        if(s[u]==0)return 0;
        if(s[u]<=k)return SS[u];
        else return (SS[u]/s[u])*k;
    }
    if(k<=s[L[u]])
        return query(L[u],l,mid,k);
    else
    {
        ret+=SS[L[u]];
        ret+=query(R[u],mid+1,r,k-s[L[u]]);
        return ret;
    }
}
int main(){
    m=read();n=read();
    int x,y,z;
    for(int i=1;i<=m;i++)
    {
        x=read();y=read();z=read();
        e[++cnt].pos=x;e[cnt].v=z;
        e[++cnt].pos=y+1;e[cnt].v=-z;
        maxx=max(maxx,z);
    }
    sort(e+1,e+cnt+1,cmp);
    for(int i=1;i<=cnt;i++)
    {
        T[i]=update(T[i-1],1,maxx,e[i].v);
    }
    for(int i=cnt;i>=1;i--)
        if(e[i].pos!=e[i+1].pos)
            to[e[i].pos]=i;
    for(int i=1;i<=n;i++)
        if(!to[i])
            to[i]=to[i-1];
    long long k;
    pre=1;
    for(int i=1;i<=n;i++)
    {
        x=read();a=read();b=read();c=read();
        k=(pre*a+b)%c+1;
        pre=query(T[to[x]],1,maxx,k);
        printf("%lld\n",pre);
    }
    return 0;
}

LLAP.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值