主席树

https://www.cnblogs.com/zyf0163/p/4749042.html  主要是看的这篇

https://blog.csdn.net/weixin_42165981/article/details/81154209

 

主席树一般还是开40倍吧。 

 

http://acm.hdu.edu.cn/showproblem.php?pid=2665

求区间第K大。注意c++中&的作用。ls[]与rs[]都是通过这个更新的。

 

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100000 + 5;

int a[N], b[N], rt[N * 20], ls[N * 20], rs[N * 20], sum[N * 20];

int n, k, tot, sz, ql, qr, x, q, T;//这些变量可以值保留tot在这命名。

void Build(int& o, int l, int r){
    o = ++tot;
    sum[o] = 0;
    if(l == r) return;
    int m = (l + r) >> 1;
    Build(ls[o], l, m);
    Build(rs[o], m + 1, r);
}
//这里的没有加val,一般就是1或-1.
void update(int& o, int l, int r, int last, int p){ o = ++tot; ls[o] = ls[last]; rs[o] = rs[last]; sum[o] = sum[last] + 1; if(l == r) return; int m = (l + r) >> 1; if(p <= m) update(ls[o], l, m, ls[last], p); else update(rs[o], m + 1, r, rs[last], p); }
//rt[tt]是后面那个线段树的根,rt[ss]是前面那个线段树的根,相减就是表示中间的序列,每个根的线段树代表的是这段序列的前缀和。
int query(int ss, int tt, int l, int r, int k) { if(l == r) return l;//这个是字母l, int m = (l + r) >> 1; int cnt = sum[ls[tt]] - sum[ls[ss]];//左子树有多少个数在这段序列中 if(k <= cnt) return query(ls[ss], ls[tt], l, m, k); //说明第K大在左子树中 else return query(rs[ss], rs[tt], m + 1, r, k - cnt);//说明在右子树中,所以要减去左子树的数量。 } void work(){ scanf("%d%d%d", &ql, &qr, &x); int ans = query(rt[ql - 1], rt[qr], 1, sz, x); printf("%d\n", b[ans]); } int main(){ scanf("%d", &T); while(T--){ scanf("%d%d", &n, &q); for(int i = 1; i <= n; i ++) scanf("%d", a + i), b[i] = a[i]; sort(b + 1, b + n + 1); sz = unique(b + 1, b + n + 1) - (b + 1); tot = 0; Build(rt[0],1, sz); //for(int i = 0; i <= 4 * n; i ++)printf("%d,rt = %d,ls = %d, rs = %d, sum = %d\n", i, rt[i], ls[i], rs[i], sum[i]); for(int i = 1; i <= n; i ++) a[i] = lower_bound(b + 1, b + sz + 1, a[i]) - b; for(int i = 1; i <= n; i ++) update(rt[i], 1, sz, rt[i - 1], a[i]); // for(int i = 0; i <= 5 * n; i ++) printf("%d,rt = %d,ls = %d, rs = %d, sum = %d\n", i, rt[i], ls[i], rs[i], sum[i]); while(q --) work(); } return 0; }

 

 

https://cn.vjudge.net/problem/HDU-6601

直接一次把前多少个找出来。如这里top = 55,表示查询前55个。

void qry(int L, int R, int l, int r) {
    if(cnt[R] - cnt[L] == 0 || top == M) return ;
    if(l == r) {
        rep(i, 0, cnt[R] - cnt[L]) if(top < M) sta[top++] = V[l - 1];
        return ;
    }
    int mid = l + r >> 1;
  
qry(ls[L], ls[R], l, mid);
  qry(rs[L], rs[R], mid + 1, r);
}

 

 

https://www.spoj.com/problems/DQUERY/en/

 

给出一段序列,然后很多查询,每次查询区间内不同数字的个数。

 

我们可以记录每个数字最后一次出现的位置。比如,5这个数字最后一次出现在位置3上,就把位置3记录的信息++(初始化为0)。比如有一个序列 1 2 2 1 3  那么我们记录信息的数列就是 0 0 1 1 1 (2最后出现的位置是位置3  1最后出现的位置是位置4  3最后出现的位置是位置5)。那么对区间 [1,5] , [2,5] , [3,5] , [4,5] , [5,5]的数字种数,我们都可以用sum[5]-sum[x-1]来求(sum数组记录的是前缀和)(前缀和之差可以用线段树或者树状数组来求)。

那么对着区间右端点会变化的题目,我们应该怎么办呢?先思考一下如果右端点有序的话,我们可以怎么做。对R不同的区间,向线段树或者树状数组中添加元素,知道右端点更新为新的R,在添加的过程中,如果这个元素之前出现过,就把之前记录的位置储存的信息 -1,然后在新的位置储存的信息 +1,这样就可以保证在新的右端点固定的区间里,记录的是数字最后一次出现的位置的信息,这样题目就解决了。

也就是说对于这个题目,我们也可以不用主席树,只要对询问排序,然后利用树状数组或者线段树就可以解决这个问题。(离线解法)

如果不对询问排序的话,我们就必须要用主席树来解决这个问题了,对每个右端点建立一个线段树。不断查询即可。(在线解法)

所以说这个题目虽然是主席树,但并不是基于权值线段树写的,每次是在出现的那个位置加一,而不是在那个值的位置加一,所以在区间查找个数的时候用的是普通线段树的求法。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 3e4 + 5;

int a[N], b[N], rt[N * 20], ls[N * 20], rs[N * 20], sum[N * 20];
int ind[N];
int n, k, tot, sz, ql, qr, x, q, T;

void Build(int& o, int l, int r){
    o = ++tot;
    sum[o] = 0;
    if(l == r) return;
    int m = (l + r) >> 1;
    Build(ls[o], l, m);
    Build(rs[o], m + 1, r);
}

void update(int& o, int l, int r, int last, int p, int v){
    o = ++tot;
    ls[o] = ls[last];
    rs[o] = rs[last];
    sum[o] = sum[last] + v;
    if(l == r) return;
    int m = (l + r) >> 1;
    if(p <= m)  update(ls[o], l, m, ls[last], p, v);
    else update(rs[o], m + 1, r, rs[last], p, v);
}

int query(int rt, int l, int r, int le, int re) {
  if(le <= l && re >= r) return sum[rt];
  int m = (l + r) >> 1;
  if(re <= m) return query(ls[rt], l, m, le, re);
  else if(le > m) return query(rs[rt], m + 1, r, le, re);
  else return query(ls[rt], l, m, le, m) + query(rs[rt], m + 1, r, m + 1, re);
}

void work(){
    scanf("%d%d", &ql, &qr);
    int ans = query(rt[qr], 1, sz, ql, qr);
    printf("%d\n", ans);
}

int main(){
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++) scanf("%d", a + i), b[i] = a[i];
    memset(ind, 0, sizeof(ind));
    sort(b + 1, b + n + 1);
    sz = unique(b + 1, b + n + 1) - (b + 1);
    tot = 0;
    Build(rt[0], 1, sz);
    int tmp;
    //for(int i = 0; i <= 4 * n; i ++)printf("%d,rt =  %d,ls =  %d, rs = %d, sum = %d\n", i, rt[i], ls[i], rs[i], sum[i]);
    for(int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + sz + 1, a[i]) - b;
    for(int i = 1; i <= n; i ++) {
        if(ind[a[i] ] == 0) update(rt[i], 1, sz, rt[i - 1], i, 1);
        else {
            update(tmp, 1, sz, rt[i - 1], ind[a[i]], -1);
            update(rt[i], 1, sz, tmp, i, 1);
        }
        ind[a[i] ] = i;
    }
//        for(int i = 0; i <= 5 * n; i ++) printf("%d,rt =  %d,ls =  %d, rs = %d, sum = %d\n", i, rt[i], ls[i], rs[i], sum[i]);
    scanf("%d", &q);
    while(q --) work();
    return 0;
}

 

 

https://cn.vjudge.net/contest/243301#problem/A

这个题的意思应该是求第K大。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include<map>
using namespace std;
const int N = 1e5 + 5;
typedef long long ll;
int aa[N], bb[N], rt[N * 20], ls[N * 20], rs[N * 20], sum[N * 20];
int tot, num;


struct Node {
    int ind;
    int a, b, c;
    Node(int ind = 0, int a = 0, int b = 0, int c = 0) : ind(ind), a(a), b(b), c(c) {};
};
Node node[2 * N];
void Build(int& o, int l, int r){
    o = ++tot;
    sum[o] = 0;
    if(l == r) return;
    int m = (l + r) >> 1;
    Build(ls[o], l, m);
    Build(rs[o], m + 1, r);
}

void update(int& o, int l, int r, int last, int p, int v){
    o = ++tot;
    ls[o] = ls[last];
    rs[o] = rs[last];
    sum[o] = sum[last] + v;
    if(l == r) return;
    int m = (l + r) >> 1;
    if(p <= m)  update(ls[o], l, m, ls[last], p, v);
    else update(rs[o], m + 1, r, rs[last], p, v);
}
//查询对应序列第K大。
int querymaxk(int ss, int tt, int l, int r, int k) {
    if(l == r) return l;
    int m = (l + r) >> 1;
    int cnt = sum[ls[tt]] - sum[ls[ss]];
    if(k <= cnt) return querymaxk(ls[ss], ls[tt], l, m, k);
    else return querymaxk(rs[ss], rs[tt], m + 1, r, k - cnt);
}
//查询值域区间有多少个数
int queryans(int root, int le, int re, int l, int r) {
    if(le <= l && re >= r) {
        return sum[root];
    }
    int m = (l + r) >> 1;
    if(re <= m) {
        return queryans(ls[root], le, re, l, m);
    }
    else if(le > m) {
        return queryans(rs[root], le, re, m + 1, r);
    }
    else {
        return queryans(ls[root], le, m, l, m) + queryans(rs[root], m + 1, re, m + 1, r);
    }
}

int main() {
    int n;
    int ca = 0;
    while(~scanf("%d", &n)) {
        char ch[10];
        int a, b, c;
        int cnt = 0;
        for(int i = 1; i <= n; i++) {
            scanf("%s", ch);
            if(ch[0] == 'I') {
                scanf("%d", &a);
                node[i] = Node(0, a, 0, 0);
                aa[++cnt] = a;
                bb[cnt] = a;
            }
            else if(ch[6] == '1') {
                scanf("%d%d%d", &a, &b, &c);
                node[i] = Node(1, a, b, c);
            }
            else if(ch[6] == '2') {
                scanf("%d", &a);
                node[i] = Node(2, a, 0, 0);
            }
            else {
                scanf("%d", &a);
                node[i] = Node(3, a, 0, 0);
            }
        }
        sort(aa + 1, aa + cnt + 1);
        tot = 0;
        int sz = unique(aa + 1, aa + cnt + 1) - aa - 1;
        Build(rt[0], 1, sz);
        for(int i = 1; i <= cnt; i++) {
            int v = lower_bound(aa + 1, aa + cnt + 1, bb[i]) - aa;
            update(rt[i], 1, sz, rt[i - 1], v, 1);
        }
        int val = 0;
        ll ans1 = 0, ans2 = 0, ans3 =0 ;
        for(int i = 1; i <= n; i++) {
            if(node[i].ind == 0) {
                val++;
            }
            else if(node[i].ind == 1) {
                int ans = querymaxk(rt[node[i].a - 1 ], rt[node[i].b ], 1, sz, node[i].c);
                ans1 += (ll)aa[ans];
            }
            else if(node[i].ind == 3) {
                int ans = querymaxk(rt[0], rt[val], 1, sz, node[i].a);
                ans3 += (ll)aa[ans];
            }
            else {
                int v = lower_bound(aa + 1, aa + cnt + 1, node[i].a) - aa;
                if(v > 1) {
                    int ans = queryans(rt[val], 1, v, 1, sz);
                    ans2 += (ll)ans;
                }
                else ans2++;
            }
        }
        printf("Case %d:\n", ++ca);
        printf("%lld\n%lld\n%lld\n", ans1, ans2, ans3);
    }
    return 0;
}

 

 

https://cn.vjudge.net/contest/243301#problem/C

 任务查询系统

题面

最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分。超级计算机中的

任务用三元组(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

Hint

样例解释

K1 = (1*1+3)%2+1 = 1

K2 = (1*2+3)%4+1 = 2

K3 = (2*8+4)%3+1 = 3

对于100%的数据,1≤m,n,Si,Ei,Ci≤100000,0≤Ai,Bi≤100000,1≤Pi≤10000000,Xi为1到n的一个排列

每个任务有个起始时间,一个终止时间,一个优先级。

利用差分的思想,我们把任务拆分成两个,一个是起始时间加上优先级,一个是终止时间加一减去优先级,

不过主席树的叶子不是表示的时间,而是表示的优先级,按照时间排序,把优先级一个一个弄进去。

然后在找某个时间点的时候,就二分找到这个时间点的位置,即找到根节点,然后找寻前K大。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include<map>
using namespace std;
const int N = 2e5 + 5;
typedef long long ll;
int rt[N * 2], ls[N * 24], rs[N * 24], aa[N];
int num[N * 24];
ll sum[N * 24];
int tot;

struct Node {
    int ind, val, p;
    Node(int ind = 0, int val = 0, int p = 0) : ind(ind), val(val), p(p) {};
    bool operator < (const Node& a) const {
        if(ind == a.ind && val == a.val) return p < a.p;
        else if(ind == a.ind) return val < a.val;
        return ind < a.ind;
    }
};
Node node[N * 2];
void pushup(int root) {
    num[root] = num[ls[root] ] + num[rs[root] ];
    sum[root] = sum[ls[root] ] + sum[rs[root] ];
}

void Build(int& o, int l, int r){
    o = ++tot;
    sum[o] = 0;
    num[o] = 0;
    if(l == r) return;
    int m = (l + r) >> 1;
    Build(ls[o], l, m);
    Build(rs[o], m + 1, r);
    pushup(o);
}

void update(int& o, int l, int r, int last, int p, int v){
    o = ++tot;
    ls[o] = ls[last];
    rs[o] = rs[last];
    if(l == r) {
        num[o] = num[last] + v;
        sum[o] = sum[last] + v * aa[p];
        return;
    }
    int m = (l + r) >> 1;
    if(p <= m)  update(ls[o], l, m, ls[last], p, v);
    else update(rs[o], m + 1, r, rs[last], p, v);
    pushup(o);
}

ll queryprek(int root, int l, int r, int k) {
    if(l == r) return min(k, num[root]) * aa[l];
    int c = num[ls[root] ];
    int m = (l + r) >> 1;
    if(k <= c) return queryprek(ls[root], l, m, k);
    else return sum[ls[root] ] + queryprek(rs[root], m + 1, r, k - num[ls[root] ]);
}

int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    int a, b, c;
    for(int i = 1; i <= n; i++) {
        scanf("%d%d%d", &a, &b, &c);
        node[i * 2 - 1] = Node(a, 1, c);
        node[i * 2] = Node(b + 1, -1, c);
        aa[i] = c;
    }
    sort(node + 1, node + 2 * n + 1);
    sort(aa + 1, aa + n + 1);
    int sz = unique(aa + 1, aa + n + 1) - (aa + 1);
    tot = 0;
    Build(rt[0], 1, sz);
    for(int i = 1; i <= 2 * n; i++) {
        node[i].p = lower_bound(aa + 1, aa + n + 1, node[i].p) - aa;
        update(rt[i], 1, sz, rt[i - 1], node[i].p, node[i].val);
    }
    int x, A, B, C;
    ll pre = 1;
    for(int i = 1; i <= m; i++) {
        scanf("%d%d%d%d", &x, &A, &B, &C);
        int k = 1 + (A * pre + B) % C;
        Node y = Node(x, 1, 1 << 30);
        int root = lower_bound(node + 1, node + 2 * n + 1, y) - node - 1;
        pre = queryprek(rt[root], 1, sz, k);
        printf("%lld\n", pre);
    }
    return 0;
}

 

https://cn.vjudge.net/contest/243301#problem/D

题意:给出n个平行于x轴的线段,m次射击,每次射击在位置(x,0),可以射击到X=x这条线上最近的K条线段,所得分数为各线段高度和,若上一次射击分数超过P,则得分双倍。

解法:这个题目以线段的高度来建立主席树。对于线段分成开始位置与结束位置,利用差分思想,把线段添加到主席树中。这个题注意主席树开大点。

 

#include <cstdio>
#include <cstring>
#include <algorithm>
#include<map>
using namespace std;
const int N = 1e5 + 5;
typedef long long ll;
int rt[2 * N], ls[N * 24], rs[N * 24], aa[N];
int num[N * 24];
ll sum[N * 24];
int tot;

struct Node {
    int ind, val, p;
    Node(int ind = 0, int val = 0, int p = 0) : ind(ind), val(val), p(p) {};
    bool operator < (const Node& a) const {
        if(ind == a.ind && val == a.val) return p < a.p;
        else if(ind == a.ind) return val < a.val;
        return ind < a.ind;
    }
};

Node node[N * 2];

void pushup(int root) {
    num[root] = num[ls[root] ] + num[rs[root] ];
    sum[root] = sum[ls[root] ] + sum[rs[root] ];
}

void Build(int& o, int l, int r){
    o = ++tot;
    sum[o] = 0;
    num[o] = 0;
    if(l == r) return;
    int m = (l + r) >> 1;
    Build(ls[o], l, m);
    Build(rs[o], m + 1, r);
    pushup(o);
}

void update(int& o, int l, int r, int last, int p, int v){
    o = ++tot;
    ls[o] = ls[last];
    rs[o] = rs[last];
    if(l == r) {
        num[o] = num[last] + v;
        sum[o] = sum[last] + v * 1LL * aa[p];
        return;
    }
    int m = (l + r) >> 1;
    if(p <= m)  update(ls[o], l, m, ls[last], p, v);
    else update(rs[o], m + 1, r, rs[last], p, v);
    pushup(o);
}

ll queryprek(int root, int l, int r, int k) {
    if(l == r) return min(k, num[root]) * 1LL * aa[l];
    int c = num[ls[root] ];
    int m = (l + r) >> 1;
    if(k <= c) return queryprek(ls[root], l, m, k);
    else return sum[ls[root] ] + queryprek(rs[root], m + 1, r, k - num[ls[root] ]);
}

int n, m, x, p;
int main() {
    while(~scanf("%d%d%d%d", &n, &m, &x, &p)) {
        int l, r, d;
        for(int i = 1; i <= n; i++) {
            scanf("%d%d%d", &l, &r, &d);
            node[2 * i - 1] = Node(l, 1, d);
            node[2 * i] = Node(r + 1, -1, d);
            aa[i] = d;
        }
        sort(node + 1, node + 2 * n + 1);
        sort(aa + 1, aa + n + 1);
        int sz = unique(aa + 1, aa + n + 1) - (aa + 1);
        tot = 0;
        Build(rt[0], 1, sz);
        for(int i = 1; i <= 2 * n; i++) {
            int k = lower_bound(aa + 1, aa + sz + 1, node[i].p) - aa;
            update(rt[i], 1, sz, rt[i - 1], k, node[i].val);
        }
        ll pre = 1, ans;
        int xx, a, b, c;
        while(m--) {
            scanf("%d%d%d%d", &xx, &a, &b, &c);
            int k = (a * pre + b) % c;
            Node y = Node(xx, 1, 1e9 + 7);
            int pos = lower_bound(node + 1, node + 2 * n + 1, y) - node - 1;
            if(k == 0 || pos == 0) {
                printf("0\n");
                pre = 0;
                continue;
            }
            ans = queryprek(rt[pos], 1, sz, k);
            if(pre > p * 1LL) ans = ans * 2;
            printf("%lld\n", ans);
            pre = ans;
        }
    }
    return 0;
}

 

 

https://cn.vjudge.net/contest/243301#problem/G

一串序列。

给定两个区间,一左一右不想交。

每个区间选一个点,求最大的中位数。

以原数列的位置建立主席树。每个位置初始为1.

然后对原数列排序,对每个数建立一个新的主席树,比他小的数的位置-1。

然后求结果时二分数的排序后的位置,求中间区间的总和,左区间的右边最大值,右区间的左边最大值,看是否加起来大于等于0.

#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
const int N = 20000 + 10;
const int inf = 0x3f3f3f3f;
int rt[N], ls[N * 40], rs[N * 40], sum[N * 40], lsum[N * 40], rsum[N * 40];

int tot;

void pushup(int rt) {
    sum[rt] = sum[ls[rt] ] + sum[rs[rt] ];
    lsum[rt] = max(lsum[ls[rt] ], sum[ls[rt] ] + lsum[rs[rt] ]);
    rsum[rt] = max(rsum[rs[rt] ], sum[rs[rt] ] + rsum[ls[rt] ]);
}

void Build(int& o, int l, int r){
    o = ++tot;
    if(l == r) {
        sum[o] = 1;
        lsum[o] = 1;
        rsum[o] = 1;
        return;
    }
    int m = (l + r) >> 1;
    Build(ls[o], l, m);
    Build(rs[o], m + 1, r);
    pushup(o);
}
//这里的没有加val,一般就是1或-1.
void update(int& o, int l, int r, int last, int p, int val){
    o = ++tot;
    ls[o] = ls[last];
    rs[o] = rs[last];
    if(l == r) {
        sum[o] = lsum[o] = rsum[o] = val;
        return;
    }
    int m = (l + r) >> 1;
    if(p <= m)  update(ls[o], l, m, ls[last], p, val);
    else update(rs[o], m + 1, r, rs[last], p, val);
    pushup(o);
}

int querysum(int rt, int l, int r, int le, int re) {
    if(le <= l && re >= r) return sum[rt];
    int m = (l + r) >> 1;
    if(re <= m) return querysum(ls[rt], l, m, le, re);
    else if(le > m) return querysum(rs[rt], m + 1, r, le, re);
    else return querysum(ls[rt], l, m, le, m) + querysum(rs[rt], m + 1, r, m + 1, re);
}

pii querylsum(int rt, int l, int r, int le, int re) {
    if(le <= l && re >= r) return pii(lsum[rt], sum[rt]);
    int m = (l + r) >> 1;
    pii tmp1 = pii(-inf, 0), tmp2 = pii(-inf, 0);
    if(re <= m) tmp1 = querylsum(ls[rt], l, m, le, re);
    else if(le > m) tmp1 = querylsum(rs[rt], m + 1, r, le, re);
    else {
        tmp1 = querylsum(ls[rt], l, m, le, m);
        tmp2 = querylsum(rs[rt], m + 1, r, m + 1, re);
    }
    if(tmp2.first == -inf) return tmp1;
    else return pii(max(tmp1.first, tmp1.second + tmp2.first), tmp1.second + tmp2.second);
}

pii queryrsum(int rt, int l, int r, int le, int re) {
    if(le <= l && re >= r) return pii(rsum[rt], sum[rt]);
    int m = (l + r) >> 1;
    pii tmp1 = pii(-inf, 0), tmp2 = pii(-inf, 0);
    if(re <= m) tmp1 = queryrsum(ls[rt], l, m, le, re);
    else if(le > m) tmp1 = queryrsum(rs[rt], m + 1, r, le, re);
    else {
        tmp1 = queryrsum(ls[rt], l, m, le, m);
        tmp2 = queryrsum(rs[rt], m + 1, r, m + 1, re);
    }
    if(tmp2.first == -inf) return tmp1;
    else return pii(max(tmp2.first, tmp2.second + tmp1.first), tmp1.second + tmp2.second);
}

int n, m;
bool check(int mid, int a, int b, int c, int d) {
    int val = 0;
    if(c - b > 1) val = querysum(rt[mid], 0, n - 1, b + 1, c - 1);
    int val2 = queryrsum(rt[mid], 0, n - 1, a, b).first;
    int val3 = querylsum(rt[mid], 0, n - 1, c, d).first;
//    printf("mid = %d %d %d %d\n", mid, val, val2, val3);
    return val + val2 + val3 >= 0;
}

int q[5];
pii p[N];
int main(){
    scanf("%d", &n);
    for(int i = 0; i < n; i++) {
        scanf("%d", &p[i].first);
        p[i].second = i;
    }
    tot = 0;
    sort(p, p + n);
    Build(rt[0], 0, n - 1);
    for(int i = 1; i < n; i++) {
        update(rt[i], 0, n - 1, rt[i - 1], p[i - 1].second, -1);
    }
    scanf("%d", &m);
    int X = 0;
    while(m--) {
        scanf("%d%d%d%d", &q[0], &q[1], &q[2], &q[3]);
        q[0] = (q[0] + X) % n;
        q[1] = (q[1] + X) % n;
        q[2] = (q[2] + X) % n;
        q[3] = (q[3] + X) % n;
        sort(q, q + 4);
//        printf("q=%d %d %d %d\n", q[0], q[1], q[2], q[3]);
        int l = 0, r = n - 1, mid, ans;
        while(l <= r) {
            mid = (l + r) >> 1;
            if(check(mid, q[0], q[1], q[2], q[3])) {
                l = mid + 1;
                ans = mid;
            }
            else r = mid - 1;
        }
        printf("%d\n", p[ans].first);
        X = p[ans].first;
    }
    return 0;
}

 

https://cn.vjudge.net/contest/243301#problem/J

一串序列,有四种操作

1.将某段区间的数加上某个值。版本号加一。

2查询当前某段区间和。

3.查询某个历史版本的区间和。

4将版本号回到某个以前版本,且之前那些在他后面的版本可以作废。(所以这时候可以修改tot值,节省空间)

 

做法。每次只记录add值,因为主席树是要继承以前版本的信息,如果按以前的区间加pushdown的话要额外增加很多节点。所以不能够每次pushdown。

每次记录add值,那么在在他上面的(即包含他的)节点要进行pushup。每次查询区间值等于sum[o] + add值,所以对于在add值下面的节点要累加add值。

 

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100000 + 5;
typedef long long ll;
int rt[N * 40], ls[N * 40], rs[N * 40];
ll sum[N * 40], add[N * 40];

int tot;//这些变量可以值保留tot在这命名。

void pushup(int rt, int m) {
    sum[rt] = sum[ls[rt] ] + sum[rs[rt] ] + add[ls[rt] ] * (m - (m >> 1)) + add[rs[rt] ] * (m >> 1);
}

void Build(int& o, int l, int r){
    o = ++tot;
    sum[o] = 0;
    add[o] = 0;
//    printf("scs\n");
    if(l == r) {
        scanf("%lld", &sum[o]);
//        printf("sum[%d] = %lld\n", o, sum[o]);
//        sum[o] = a[l];
        add[o] = 0;
        return;
    }
    int m = (l + r) >> 1;
    Build(ls[o], l, m);
    Build(rs[o], m + 1, r);
//    pushup(o, r - l + 1);
    sum[o] = sum[ls[o] ] + sum[rs[o] ];
}
//这里的没有加val,一般就是1或-1.
void update(int& o, int l, int r, int last, int le, int re, ll d){
    o = ++tot;
    ls[o] = ls[last];
    rs[o] = rs[last];
    sum[o] = sum[last];
    add[o] = add[last];
    if(le <= l && re >= r) {
        add[o] += d;
        return;
    }
    int m = (l + r) >> 1;
    if(re <= m) update(ls[o], l, m, ls[last], le, re, d);
    else if(le > m) update(rs[o], m + 1, r, rs[last], le, re, d);
    else {
        update(ls[o], l, m, ls[last], le, m, d);
        update(rs[o], m + 1, r, rs[last], m + 1, re, d);
    }
    pushup(o, r - l + 1);
//    if(p <= m)  update(ls[o], l, m, ls[last], p);
//    else update(rs[o], m + 1, r, rs[last], p);
}

ll query(int o, int l, int r, int le, int re, ll d) {
    if(le <= l && re >= r) {
//        printf("%d %d %lld %lld %lld\n", l, r, sum[o], d, add[o]);
        return sum[o] + (r - l + 1) * (d + add[o]);
    }
    int m = (l + r) >> 1;
    if(re <= m) return query(ls[o], l, m, le, re, add[o] + d);
    else if(le > m) return query(rs[o], m + 1, r, le, re, add[o] + d);
    else {
        return query(ls[o], l, m, le, m, add[o] + d) + query(rs[o], m + 1, r, m + 1, re, add[o] + d);
    }
}

int main(){
    int n, m;
    int ca = 0;
    while(~scanf("%d%d", &n, &m)){
        tot = 0;
        if(ca) printf("\n");
        ca++;
//        printf("scccc\n");
//        for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
        Build(rt[0], 1, n);
        char ch[3];
        int l, r, t;
        ll d;
        int time = 0;
        while(m--) {
            scanf("%s", ch);
            if(ch[0] == 'C') {
                scanf("%d%d%lld", &l, &r, &d);
                time++;
                update(rt[time], 1, n, rt[time - 1], l, r, d);
            }
            else if(ch[0] == 'Q') {
                scanf("%d%d", &l, &r);
                printf("%lld\n", query(rt[time], 1, n, l, r, 0));
            }
            else if(ch[0] == 'H') {
                scanf("%d%d%d", &l, &r, &t);
                printf("%lld\n", query(rt[t], 1, n, l, r, 0));
            }
            else {
                scanf("%d", &t);
                time = t;
                tot = rt[time + 1] - 1;
            }
        }
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/downrainsun/p/11200405.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值