主席树进阶

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:

\[l_i = min\{ (l_i^`+ans_{i-1})\ mod \ n+1, (r_i^`+ans_{i-1})\ mod \ n+1 \} \]

\[r_i = max\{ (l_i^`+ans_{i-1})\ mod \ n+1, (r_i^`+ans_{i-1})\ mod \ n+1 \} \]

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;
}
posted @ 2019-08-03 14:43  Artoriax  阅读(221)  评论(0编辑  收藏  举报