[CQOI2015]任务查询系统

# 洛谷题目链接:[[CQOI2015]任务查询系统](https://www.luogu.org/problemnew/show/P3168)

题目描述

最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分。超级计算机中的任务用三元组(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行,每行一个整数,表示查询结果。

输入输出样例

输入样例#1:

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

输出样例#1:

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的一个排列

简述一下题意:给出一个区间(长度最长为(10^6)),以及m次区间修改和区间的大小n.然后是n次询问,求出包含x位置的所有区间中的前k个的和.强制在线.

因为序列的长度并不长,所以我们可以考虑用一些数据结构来记录下覆盖点下标为pos的所有区间.然后还要求前k小的和,这里可以用主席树来维护.

下面直接讲一下做法吧:主席树+差分

  • 将每个任务看作是在区间[l,r]上加一个值(区间修改).
  • 将一个区间[l,r]的修改为在下标为l的位置+1,在r+1的位置-1.
  • 将所有修改的位置按照位置顺序排序,每个下标都储存一些区间修改的值.
  • 在修改主席树和的时候直接加上一次修改的值乘以它的符号(前面记录的加或减),这样就统计出了每个位置的和.
  • 统计时直接按照主席树的查询方式查询.

因为一个下标的位置上有可能有多次修改,所以要一起记录到同一个下标位置上,但是又只需要记录n个版本的主席树,所以可以用一个数组辅助一下.

下面贴一下代码

#include<bits/stdc++.h>
using namespace std;
const int N=100000+5;
typedef long long lol;

int n, m, cnt = 0, cntm = 0, size;
int w[N*2], rk[N*2];
int root[N], c[N*2];

struct task{
    int pos, f, val;
}mi[N*3];

struct president_tree{
    int ls, rs, sz;
    lol sum;
}t[N*40];//数组开大点

int gi(){
    int ans = 0 , f = 1; char i = getchar();
    while(i<'0'||i>'9'){if(i=='-')f=-1;i=getchar();}
    while(i>='0'&&i<='9'){ans=ans*10+i-'0';i=getchar();}
    return ans * f;
}

bool cmp(task a,task b){
    return a.pos < b.pos;
}

void updata(int &node,int last,int f,int pos,int l=1,int r=m){
    node = ++cnt; t[node] = t[last];
    t[node].sum += (lol) f*w[pos]; t[node].sz += f;
    if(l == r) return; int mid = (l+r>>1);
    if(pos <= mid) updata(t[node].ls , t[last].ls , f , pos , l , mid);
    else updata(t[node].rs , t[last].rs , f , pos , mid+1 , r);
}

lol query(int node,int k,int l=1,int r=m){
    if(l == r) return t[node].sum/t[node].sz*(lol)k;
    int mid = (l+r>>1), s = t[t[node].ls].sz;
    if(k <= s) return query(t[node].ls,k,l,mid);
    else return t[t[node].ls].sum + query(t[node].rs,k-s,mid+1,r);
}

int main(){
    //freopen("data.in","r",stdin);
    int x, y, z, pos; n = gi(); m = gi();
    for(int i=1;i<=n;i++){
		x = gi(); y = gi(); z = gi();
		mi[++cntm].pos = x;   mi[cntm].f = 1;  mi[cntm].val = z;
		mi[++cntm].pos = y+1; mi[cntm].f = -1; mi[cntm].val = z;//记录每个位置上的修改
		w[i] = z;
    }
    sort(w+1 , w+n+1); sort(mi+1 , mi+cntm+1 , cmp);
    for(int i=1;i<=cntm;i++)
		rk[i] = lower_bound(w+1,w+n+1,mi[i].val)-w;
	c[0] = 0;
	for(int i=1, j=1;i<=m;i++){
		while(j<=cntm && mi[j].pos == i)
		    updata(c[j] , c[j-1] , mi[j].f , rk[j]) , j++;//将同一位置上的修改加到同一个下标
		root[i] = c[j-1];
    }
    lol lans = 1, kth;
    for(int i=1;i<=m;i++){
		pos = gi(); x = gi(); y = gi(); z = gi();
		kth = (x*lans+y) % z + 1;
		if(t[root[pos]].sz <= kth) lans = t[root[pos]].sum;
		else lans = (lol) query(root[pos],kth);
		//printf("kth=%lld size=%d
",kth,t[root[pos]].sz);
		printf("%lld
",lans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/BCOI/p/8807187.html