P3168 [CQOI2015] 任务查询系统(差分 + 主席树)

文章描述了一个在线任务查询系统的问题,其中任务有优先级和时间范围,需要维护每个时刻优先级最小的k个任务的优先级之和。解决方案是使用动态维护的线段树(主席树)数据结构,通过时间差分处理任务的开始和结束,高效地进行查询操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

P3168 [CQOI2015] 任务查询系统

题意

m m m 个任务,第 i i i 个任务在时间 [ s i , e i ] [s_i,e_i] [si,ei] 内运行,优先级为 p i p_i pi。时间范围是 [ 1 , n ] [1,n] [1,n] n n n 次查询,查询第 x x x 秒的所有任务当中,优先级最小的 k k k 个任务的优先级之和。强制在线。

思路

对时间差分。

假设我们可以维护任何一个时刻建的任何优先级。用 c n t cnt cnt s u m sum sum 记录这个时刻的这个优先级的 “个数变化” 以及 “个数变化乘以优先级”。时间 [ s i , e i ] [s_i,e_i] [si,ei] 内有一个权值为 p i p_i pi 的任务,分别处理时刻 s i s_i si 和时刻 e i + 1 e_i+1 ei+1

  • 时间 s i s_i si 新开始了一个任务,所以时刻 s i s_i si c n t cnt cnt + 1 +1 +1 s u m sum sum + p i +p_i +pi
  • 时间 e i + 1 e_i+1 ei+1 结束了一个任务,所以时刻 e i + 1 e_i+1 ei+1 c n t cnt cnt − 1 -1 1 s u m sum sum − p i -p_i pi

当我们想要查询时刻 x x x 的任务状态时,其实就是对于每一个优先级,都从 [ 1 , x ] [1,x] [1,x] 将所有 c n t cnt cnt s u m sum sum 求和。这样我们就可以得到了时刻 x x x 每一个优先级的任务数量,以及任务数量和优先级的乘积。

那么怎样把这个二维信息的维护实现呢?就是利用主席树。每一个时刻开一棵线段树,下标是优先级,权值是 c n t cnt cnt s u m sum sum。所有时刻的这 n n n 棵线段树就构成了一棵主席树,因为后一个时刻总是在前一个时刻的基础上插入若干个点。

代码实现如下。

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<int, int>
#define endl "\n"
/**********************  Core code begins  **********************/

struct segtrs {
	struct Info {
		int l = 0, r = 0, cnt = 0, sum = 0;
	};
	vector<Info> info;
	int cnt = 0;
	segtrs(int n): info(n * 64) {}
	// 新开一条链,在位置 x 处的权值 += k
	int insert(int p, int l, int r, int x, int k) {
		int rt = ++cnt;
		info[rt] = info[p];
		info[rt].cnt += k;
		info[rt].sum += x * k;
		if (l == r) {
			return rt;
		}
		int mid = (l + r) >> 1;
		if (x <= mid) {
			info[rt].l = insert(info[p].l, l, mid, x, k);
		} else {
			info[rt].r = insert(info[p].r, mid + 1, r, x, k);
		}
		return rt;
	}
	// 查询优先级前 k 小的任务优先级之和
	int query(int p, int l, int r, int k) {
		k = min(k, info[p].cnt);
		if (l == r) {
			return k * l;
		}
		int mid = (l + r) >> 1;
		int lp = info[p].l, rp = info[p].r;
		if (info[lp].cnt >= k) {
			return query(lp, l, mid, k);
		} else {
			return info[lp].sum + query(rp, mid + 1, r, k - info[lp].cnt);
		}
	}
};

void SolveTest() {
	int m, n;
	cin >> m >> n;
	struct Task {
		int t, p, flg;		// 时间点,优先级,开始/结束标志 	
	};
	vector<Task> task;
	for (int i = 1; i <= m; i++) {
		int s, e, p;
		cin >> s >> e >> p;
		task.push_back({s, p, 1});
		task.push_back({e + 1, p, -1});
	}

	sort(task.begin(), task.end(), [](auto x, auto y) {
		return x.t < y.t;
	});

	const int M = 1e7 + 7; 	//	优先级的范围
	vector<int> root(n + 2);
	segtrs tr(n + 2);

	int nowt = 0;
	for (int i = 0; i < task.size(); i++) {
		if (task[i].t == nowt) {
			root[nowt] = tr.insert(root[nowt], 1, M, task[i].p, task[i].flg);
		} else {
			while (nowt < task[i].t - 1) {
				nowt++;
				root[nowt] = root[nowt - 1];
			}
			nowt++;
			root[nowt] = tr.insert(root[nowt - 1], 1, M, task[i].p, task[i].flg);
		}
	}

	while (nowt < n + 1) {
		nowt++;
		root[nowt] = root[nowt - 1];
	}

	int q = n, last = 1;
	while (q--) {
		int x, a, b, c;
		cin >> x >> a >> b >> c;
		int k = 1 + (a * last + b) % c;
		last = tr.query(root[x], 1, M, k);
		cout << last << endl;
	}
}

/**********************  Core code ends  ***********************/
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int T = 1;
	// cin >> T;
	for (int i = 1; i <= T; i++) {
		SolveTest();
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值