problem
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(i−1),不然根本查的就是一个空线段树版本。
-
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;
}
本文介绍了一种使用主席树解决洛谷P3168问题的方法。通过对任务进行分解,并利用主席树的数据结构特性,实现了对某一秒内任务变化的有效查询。文章详细介绍了算法的设计思路及实现细节。
732

被折叠的 条评论
为什么被折叠?



