Description
最近实验室正在为其管理的超级计算机编制一套任务管理系统, 而你被安排完成其中的查询部分. 超级计算机中的任务用三元组 (Si, Ei, Pi) 描述, (Si, Ei, Pi) 表示任务从第 Si 秒开始, 在第 Ei 秒后结束 (第 Si 秒和 Ei 秒任务也在运行), 其优先级为 Pi. 同一时间可能有多个任务同时执行, 它们的优先级可能相同, 也可能不同. 调度系统会经常向 查询系统询问, 第 Xi 秒正在运行的任务中, 优先级最小的 Ki 个任务 (即将任务按照优先级从小到大排序后取前 Ki 个) 的优先级之和是多少. 特别的, 如果 Ki 大于第 Xi 秒正在运行的任务总数, 则直接回答第 Xi 秒正在运行的任务优先 级之和. 上述所有参数均为整数, 时间的范围在 1 到 n 之间 (包含 1 和 n).
Input
输入文件第一行包含两个空格分开的正整数 m 和 n, 分别表示任务总数和时间范围. 接下来 m 行, 每行包含三个空格 分开的正整数 Si、Ei 和 Pi (Si≤Ei), 描述一个任务. 接下来 n 行, 每行包含四个空格分开的整数 Xi、Ai、Bi 和 Ci, 描述一次查询. 查询的参数 Ki 需要由公式 Ki=1+(Ai*Pre+Bi) mod Ci 计算得到. 其中 Pre 表示上一次查询的结果, 对于第一次查询, Pre=1.
Output
输出共 n 行, 每行一个整数, 表示查询结果.
Sample Input
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
Sample Output
2
8
11
Sample Explanation
K1=(1*1+3)% 2+1=1
K2=(1*2+3)% 4+1=2
K3=(2*8+4)% 3+1=3
Data Range
对于 100% 的数据, 1≤m, n, Si, Ei, Ci≤100000, 0≤Ai, Bi≤100000, 1≤Pi≤10000000, Xi 为 1 到 n 的一个排列
Solution
一道裸的可持久化线段树, 直接树状数组套一棵线段树 (内存 \(O(n \cdot log(n))\), 不用问了).
但是套树状数组需要多一个 log......
Source Code
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long lli;
const int maxn = 100005, maxm = 20000005;
#define lowest_digit(__x) ((__x)&(-__x))
int n, m;
class RetroactiveSegmentTree
{
public:
int cnt, mx;
// Vector-like random access container
int vec[maxn], top;
// Tree structure operations
int root[maxn], size[maxm], ch[maxm][2], value[maxn];
lli sum[maxm];
int insert(int x, int val, int combo)
{
// Creating new node at #x.
if (!x) x = ++cnt;
size[x] += combo;
sum[x] += combo * value[val];
int tmp = x;
// Creating subtrees
int l = 1, r = 2 * m;
while (l != r) {
int mid = (l + r) / 2, t = 0;
if (val > mid) t ^= 1, l = mid + 1;
else r = mid;
if (!ch[x][t]) ch[x][t] = ++cnt;
x = ch[x][t]; size[x] += combo;
sum[x] += combo * value[val];
}
return tmp;
}
void update(int l, int r, int pos)
{
for (int i = l; i <= n; i += lowest_digit(i))
root[i] = insert(root[i], pos, 1);
for (int i = r + 1; i <= n; i += lowest_digit(i))
root[i] = insert(root[i], pos, -1);
return ;
}
lli query(int x, int K)
{
lli res = 0, nd_cnt = 0; // Result and node count
top = 0;
for (int i = x; i != 0; i -= lowest_digit(i)) {
vec[++top] = root[i];
nd_cnt += size[root[i]];
res += sum[root[i]];
}
// That's a preliminary check if K....
if (K > nd_cnt)
return res;
else res = 0;
// Iterating through all possible / affected sub-segment-trees
int l = 1, r = 2 * m;
while (l != r) {
int mid = (l + r) / 2, t, tmp = 0;
// Actual children in possession
for (int i = 1; i <= top; i++)
tmp += size[ch[vec[i]][0]];
// Determine whether to traverse to left / right child
if (tmp < K) {
for (int i = 1; i <= top; i++)
res += sum[ch[vec[i]][0]];
t = 1;
K -= tmp; l = mid + 1;
} else {
t = 0;
r = mid;
}
// Batch manipulating children pointer locations
for (int i = 1; i <= top; i++)
vec[i] = ch[vec[i]][t];
}
res += 1 * K * value[l];
return res;
}
} st;
lli S[maxn], E[maxn], P[maxn];
int main(int argc, char** argv)
{
scanf("%d%d", &m, &n);
for (int i = 1; i <= m; i++) {
scanf("%lld%lld%lld", &S[i], &E[i], &P[i]);
st.value[i] = P[i];
}
sort(st.value + 1, st.value + m + 1);
// Preprocessing data.
for (int i = 1; i <= m; i++) {
// Get the position of p...
P[i] = lower_bound(st.value + 1, st.value + m + 1, P[i]) - st.value;
st.update(S[i], E[i], P[i]);
}
lli pre = 1, X, A, B, C, K;
for (int i = 1; i <= n; i++) {
scanf("%lld%lld%lld%lld", &X, &A, &B, &C);
K = 1 + (A * pre + B) % C;
pre = st.query(X, K);
printf("%lld\n", pre);
}
return 0;
}