[CQOI2015] 任务查询系统(主席树)

本文介绍了一种使用主席树解决洛谷P3168问题的方法。通过对任务进行分解,并利用主席树的数据结构特性,实现了对某一秒内任务变化的有效查询。文章详细介绍了算法的设计思路及实现细节。

problem

luogu-P3168

solution

主席树板题。

将一个任务拆成 l i l_i li 秒开始, r i + 1 r_i+1 ri+1 秒结束的两个任务。

但不建议以每一秒作为一个主席树版本,因为一秒中可能有若干个任务开始或结束。

不妨将所有任务按时刻排序,然后以每个任务作为一个版本。

一次只加入一个单点才是真的板子该有的样子!

然而询问的时候问的是某一秒,所以我们定义一个 E n d ( i ) End(i) End(i) 表示 i i i 秒结束后对应的最后一个任务版本。

这道题其实不难,但我调了一个晚上!

  • 某一秒可能没有任何任务的变化,所以根据写法问题,我必须 E n d ( i ) = E n d ( i − 1 ) End(i)=End(i-1) End(i)=End(i1),不然根本查的就是一个空线段树版本。

  • x x x 版本的前 k k k 小查询。可能有多个任务的权值相同,这意味着线段树上的叶子节点存的个数可能 > 1 >1 >1

    我不能访问到叶子节点就直接返回叶子节点的权值和,而应该是当前的 k ′ × k'\times k× 叶子节点村的权值。

code

#include <bits/stdc++.h>
using namespace std;
#define maxn 200005
#define int long long
int m, n, ans, cnt;
int root[maxn], End[maxn], val[maxn];
struct option { int x, v, k; }g[maxn];
struct node { int cnt, sum, lson, rson; }t[maxn * 40];

void modify( int lst, int &now, int l, int r, int p, int k ) {
	now = ++ cnt, t[now] = t[lst];
	if( l == r ) { t[now].cnt += k; t[now].sum += k * val[p]; return; };
	int mid = l + r >> 1;
	if( p <= mid ) modify( t[lst].lson, t[now].lson, l, mid, p, k );
	else modify( t[lst].rson, t[now].rson, mid + 1, r, p, k );
	t[now].cnt = t[t[now].lson].cnt + t[t[now].rson].cnt;
	t[now].sum = t[t[now].lson].sum + t[t[now].rson].sum;
}

int query( int now, int l, int r, int k ) {
	if( t[now].cnt <= k ) return t[now].sum;
	if( l == r ) return k * val[l];
	int mid = l + r >> 1;
	if( k <= t[t[now].lson].cnt ) return query( t[now].lson, l, mid, k );
	else return query( t[now].rson, mid + 1, r, k - t[t[now].lson].cnt ) + t[t[now].lson].sum;
}

signed main() {
	scanf( "%lld %lld", &m, &n );
	for( int i= 1, l, r, k;i <= m;i ++ ) {
		scanf( "%lld %lld %lld", &l, &r, &k );
		g[i] = (option){ l, k, 1 };
		g[i + m] = (option){ r + 1, k, -1 };
		val[i] = k;
	}
	sort( val + 1, val + m + 1 );
	int tot = unique( val + 1, val + m + 1 ) - val - 1;
	m <<= 1;
	sort( g + 1, g + m + 1, []( option a, option b ) { return a.x < b.x; } );
	for( int i = 1;i <= m;i ++ ) g[i].v = lower_bound( val + 1, val + tot + 1, g[i].v ) - val;
	for( int i = 1, j = 1;i <= n;i ++ ) {
		End[i] = End[i - 1];
		for( ;j <= m and g[j].x <= i;j ++ ) {
			modify( root[j - 1], root[j], 1, tot, g[j].v, g[j].k );
			End[i] = j;
		}
	}
	ans = 1;
	for( int i = 1, x, a, b, c;i <= n;i ++ ) {
		scanf( "%lld %lld %lld %lld", &x, &a, &b, &c );
		int k = ( a * ans + b ) % c + 1;
		printf( "%lld\n", ans = query( root[End[x]], 1, tot, k ) );
	}
	return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值