主席树进阶
HDU-4348 To the moon
Description
You‘ve been given N integers A [1], A [2],..., A [N]. On these integers, you need to implement the following operations:
1. C l r d: Adding a constant d for every {A i | l <= i <= r}, and increase the time stamp by 1, this is the only operation that will cause the time stamp increase.
2. Q l r: Querying the current sum of {A i | l <= i <= r}.
3. H l r t: Querying a history sum of {A i | l <= i <= r} in time t.
4. B t: Back to time t. And once you decide return to a past, you can never be access to a forward edition anymore.
.. N, M ≤ 10 5, |A [i]| ≤ 10 9, 1 ≤ l ≤ r ≤ N, |d| ≤ 10 4 .. the system start from time 0, and the first modification is in time 1, t ≥ 0, and won't introduce you to a future state.
Input
n m
A 1 A 2 ... A n
... (here following the m operations. )
Output
... (for each query, simply print the result. )
Sample Input
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4
2 4
0 0
C 1 1 1
C 2 2 -1
Q 1 2
H 1 2 1
Sample Output
4
55
9
15
0
1
题解
主席树+标记永久化,如果添加标记后每次都pushdown,对于主席树来说,每次pushdown都要新建一条链上的节点,如果pushdown我们的空间复杂度是不够的,所以我们要标记永久化,在查询的时候我们记录一下从根节点开始的标记是多少,每次统计答案时加上标记的贡献即可,回退版本时可以直接修改cnt为t+1版本的cnt,因为回退版本后不能返回,可以减少空间
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, q;
const int N = 1e5 + 10;
ll a[N];
int L[N * 30], R[N * 30], T[N];
ll sum[N * 30];
ll addv[N * 30];
int cnt;
void pushup(int rt, int l, int r) {
sum[rt] = sum[L[rt]] + sum[R[rt]] + addv[rt] * (r - l + 1);
}
int build(int l, int r) {
int rt = ++cnt;
if (l == r) {
sum[rt] = a[l];
return rt;
}
int mid = (l + r) >> 1;
if (l < r) {
L[rt] = build(l, mid);
R[rt] = build(mid + 1, r);
}
pushup(rt, l, r);
return rt;
}
int update(int pre, int l, int r, int ql, int qr, ll v) {
int rt = ++cnt;
int mid = (l + r) >> 1;
L[rt] = L[pre], R[rt] = R[pre], sum[rt] = sum[pre], addv[rt] = addv[pre];
if (ql <= l && r <= qr) {
sum[rt] += (ll)(r - l + 1) * v;
addv[rt] += v;
return rt;
}
if (ql <= mid) L[rt] = update(L[pre], l, mid, ql, qr, v);
if (qr > mid) R[rt] = update(R[pre], mid + 1, r, ql, qr, v);
pushup(rt, l, r);
return rt;
}
ll query(int rt, int l, int r, int ql, int qr, ll tot) {
if (ql <= l && r <= qr) {
return sum[rt] + tot * (r - l + 1);
}
tot += addv[rt];
int mid = (l + r) >> 1;
ll ans = 0;
//pushdown(rt, l, r, L[rt], R[rt]);
if (ql <= mid) ans += query(L[rt], l, mid, ql, qr, tot);
if (qr > mid) ans += query(R[rt], mid + 1, r, ql ,qr, tot);
return ans;
}
int main() {
while (~scanf("%d%d", &n, &q)) {
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
}
cnt = 0;
memset(sum, 0, sizeof(sum));
memset(addv, 0, sizeof(addv));
T[0] = build(1, n);
int now = 0;
for (int i = 1; i <= q; i++) {
char ch[2];
int l, r; ll v;
int t;
scanf("%s", ch);
if (ch[0] == 'Q') {
scanf("%d%d", &l, &r);
printf("%lld\n", query(T[now], 1, n, l, r, 0));
}
if (ch[0] == 'C') {
scanf("%d%d%lld", &l, &r, &v);
now++;
T[now] = update(T[now - 1], 1, n, l, r, v);
}
if (ch[0] == 'H') {
scanf("%d%d%d", &l, &r, &t);
printf("%lld\n", query(T[t], 1, n, l, r, 0));
}
if (ch[0] == 'B') {
scanf("%d", &t);
now = t;
cnt = T[now + 1];
}
}
}
return 0;
}
HDU-6278 Just \(h\)-index
Description
The \(h\)-index of an author is the largest \(h\) where he has at least \(h\) papers with citations not less than \(h\).
Bobo has published \(n\) papers with citations \(a_1, a_2, \dots, a_n\) respectively.
One day, he raises \(q\) questions. The \(i\)-th question is described by two integers \(l_i\) and \(r_i\), asking the \(h\)-index of Bobo if has only published papers with citations \(a_{l_i}, a_{l_i + 1}, \dots, a_{r_i}\).
Input
The input consists of several test cases and is terminated by end-of-file.
The first line of each test case contains two integers \(n\) and \(q\).
The second line contains \(n\) integers \(a_1, a_2, \dots, a_n\).
The \(i\)-th of last \(q\) lines contains two integers \(l_i\) and \(r_i\).
Output
For each question, print an integer which denotes the answer.
## Constraint
* \(1 \leq n, q \leq 10^5\)
* \(1 \leq a_i \leq n\)
* \(1 \leq l_i \leq r_i \leq n\)
* The sum of \(n\) does not exceed \(250,000\).
* The sum of \(q\) does not exceed \(250,000\).
Sample Input
5 3
1 5 3 2 1
1 3
2 4
1 5
5 1
1 2 3 4 5
1 5
Sample Output
2
2
2
3
题解
二分答案+主席树,水题
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, m;
const int N = 1e5 + 10;
int a[N];
int L[N * 20], R[N * 20], T[N];
int sum[N * 20];
int b[N];
int cnt;
int build(int l, int r) {
int rt = ++cnt;
sum[rt] = 0;
int mid = (l + r) >> 1;
if (l < r) {
L[rt] = build(l, mid);
R[rt] = build(mid + 1, r);
}
return rt;
}
int update(int pre, int l, int r, int x) {
int rt = ++cnt;
int mid = (l + r) >> 1;
L[rt] = L[pre], R[rt] = R[pre], sum[rt] = sum[pre] + 1;
if (l < r) {
if (x <= mid) L[rt] = update(L[pre], l, mid, x);
else R[rt] = update(R[pre], mid + 1, r, x);
}
return rt;
}
int query(int u, int v, int l, int r, int k) {
if (l >= r) return l;
int mid = (l + r) >> 1;
int x = sum[L[v]] - sum[L[u]];
if (x >= k) return query(L[u], L[v], l, mid, k);
else return query(R[u], R[v], mid + 1, r, k - x);
}
int main() {
while (~scanf("%d%d", &n, &m)) {
cnt = 0;
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
b[i] = a[i];
}
sort(b + 1, b + n + 1);
int cnt1 = unique(b + 1, b + n + 1) - b - 1;
T[0] = build(1, cnt1);
for (int i = 1; i <= n; i++) {
a[i] = lower_bound(b + 1, b + cnt1 + 1, a[i]) - b;
T[i] = update(T[i - 1], 1, cnt1, a[i]);
}
while (m--) {
int x, y;
scanf("%d%d", &x, &y);
int l = 1, r = y - x + 1;
int ans = 0;
while (l <= r) {
int mid = (l + r) >> 1;
if (b[query(T[x - 1], T[y], 1, cnt1, y - x + 2 - mid)] >= mid) {
ans = max(ans, mid);
l = mid + 1;
}
else r = mid - 1;
}
printf("%d\n", ans);
}
}
return 0;
}
HDU-5919 Sequence II
Description
Mr. Frog has an integer sequence of length n, which can be denoted as \(a_1,a_2,\cdots ,a_n\) There are m queries.
In the i-th query, you are given two integers \(l_i\) and \(r_i\). Consider the subsequence $a_{l_i},a_{l_{i+1}},a_{l_{i+2}},\cdots ,a_{r_i} $.
We can denote the positions(the positions according to the original sequence) where an integer appears first in this subsequence as \(p_{1}^{(i)},p_{2}^{(i)},\cdots, p_{k_i}^{(i)}\) (in ascending order, i.e.,\(p_{1}^{(i)}<p_{2}^{(i)}<\cdots <p_{k_i}^{(i)}\)).
Note that \(k_i\) is the number of different integers in this subsequence. You should output \(p_{\left \lceil \frac{k_i}{2} \right \rceil}^{(i)}\)for the i-th query.
Input
In the first line of input, there is an integer T (\(T\leq 2\)) denoting the number of test cases.
Each test case starts with two integers n (\(n \leq 2 \times 10^5\)) and m (\(m\leq 2\times 10^5\)). There are n integers in the next line, which indicate the integers in the sequence(i.e., \(a_1,a_2,\cdots ,a_n, 0\leq a_i \leq 2 \times 10^5\)).
There are two integers \(l_i\) and \(r_i\) in the following m lines.
However, Mr. Frog thought that this problem was too young too simple so he became angry. He modified each query to \(l_i^`,r_i^`(1\leq l_i^` \leq n,1\leq r_i^` \leq n )\). As a result, the problem became more exciting.
We can denote the answers as \(ans_1, ans_2,\cdots ,ans_m\). Note that for each test case \(ans_0 = 0\).
You can get the correct input \(l_i,r_i\) from what you read (we denote them as \(l_i^`,r_i^`\))by the following formula:
Output
You should output one single line for each test case.
For each test case, output one line “Case #x: \(p_1,p_2,\cdots ,p_m\)”, where x is the case number (starting from 1) and \(p_1,p_2,\cdots ,p_m\) is the answer.
Sample Input
2
5 2
3 3 1 5 4
2 2
4 4
5 2
2 5 2 1 2
2 3
2 4
Sample Output
Case #1: 3 3
Case #2: 3 1
Hint
题解
将主席树倒着插入,可以统计区间[l,r]的数的种类,具体方法是:
维护一个pos数组,pos[a[i]]记录a[i]出现的最后的位置(倒着插入,即最靠左的位置),主席树则维护每一个位置有没有数,而不是以数的值为主席树下标。这样我们插入一个数时,如果pos[a[i]]==0,z,则直接在i位置+1,否则先将pos[a[i]]位置-1,再将i位置+1,这样区间端点\(l\)对应的主席树就维护了l之后的数的种数,我们只要查询[l,r]内有多少数就可以了,查询完之后再这颗树上求第\((sum + 1) / 2\)大的下标是多少就可以
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, m;
const int N = 2e5 + 100;
int a[N];
int L[N * 40], R[N * 40], T[N];
int sum[N * 40];
int cnt;
int getnum() {
int ans = 0; char c;
while (!isdigit(c = getchar()));
ans = c - '0';
while (isdigit(c = getchar())) ans = ans * 10 + c - '0';
return ans;
}
int build(int l, int r) {
int rt = ++cnt;
sum[rt] = 0;
int mid = (l + r) >> 1;
if (l < r) {
L[rt] = build(l, mid);
R[rt] = build(mid + 1, r);
}
return rt;
}
int update(int pre, int l, int r, int x, int v) {
int rt = ++cnt;
int mid = (l + r) >> 1;
L[rt] = L[pre], R[rt] = R[pre], sum[rt] = sum[pre] + v;
if (l < r) {
if (x <= mid) L[rt] = update(L[pre], l, mid, x, v);
else R[rt] = update(R[pre], mid + 1, r, x, v);
}
return rt;
}
int querysum(int rt, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) {
return sum[rt];
}
int mid = (l + r) >> 1;
int ans = 0;
if (ql <= mid) ans += querysum(L[rt], l, mid, ql, qr);
if (qr > mid) ans += querysum(R[rt], mid + 1, r, ql, qr);
return ans;
}
int queryk(int v, int l, int r, int k) {
if (l >= r) return l;
int mid = (l + r) >> 1;
int x = sum[L[v]];
if (x >= k) return queryk(L[v], l, mid, k);
else return queryk(R[v], mid + 1, r, k - x);
}
int pos[N];
int main() {
int t;
t = getnum();
int cse = 0;
//freopen("ans.txt", "w", stdout);
while (t--) {
n = getnum(), m = getnum();
cnt = 0;
for (int i = 1; i <= n; i++) {
a[i] = getnum();
}
T[n + 1] = build(1, n);
memset(pos, 0, sizeof(pos));
for (int i = n; i >= 1; i--) {
if (!pos[a[i]]) {
T[i] = update(T[i + 1], 1, n, i, 1);
pos[a[i]] = i;
}
else {
T[i] = update(T[i + 1], 1, n, i, 1);
T[i] = update(T[i], 1, n, pos[a[i]], -1);
pos[a[i]] = i;
}
}
int ans = 0;
cse++;
printf("Case #%d:", cse);
while (m--) {
int l, r;
l = getnum(), r = getnum();
l = (l + ans) % n + 1;
r = (r + ans) % n + 1;
if (l > r) swap(l, r);
int num = querysum(T[l], 1, n, l, r);
printf(" %d", ans = queryk(T[l], 1, n, (num + 1) / 2));
}
printf("\n");
}
return 0;
}