zoukankan      html  css  js  c++  java
  • 主席树

    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 ", 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 ", 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 ", 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
    ", 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
    ", 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
    ", 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:
    ", ++ca);
            printf("%lld
    %lld
    %lld
    ", 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
    ", 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
    ");
                    pre = 0;
                    continue;
                }
                ans = queryprek(rt[pos], 1, sz, k);
                if(pre > p * 1LL) ans = ans * 2;
                printf("%lld
    ", 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
    ", 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
    ", 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
    ", 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
    ");
        if(l == r) {
            scanf("%lld", &sum[o]);
    //        printf("sum[%d] = %lld
    ", 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
    ", 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("
    ");
            ca++;
    //        printf("scccc
    ");
    //        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
    ", query(rt[time], 1, n, l, r, 0));
                }
                else if(ch[0] == 'H') {
                    scanf("%d%d%d", &l, &r, &t);
                    printf("%lld
    ", query(rt[t], 1, n, l, r, 0));
                }
                else {
                    scanf("%d", &t);
                    time = t;
                    tot = rt[time + 1] - 1;
                }
            }
        }
        return 0;
    }
  • 相关阅读:
    心情日记:【原创诗歌】怆情吟
    心情日记:2008年3月3日 奶奶去世
    心情日记:健身日记
    金融基础概念期货
    FXDD点值获利计算
    外汇基础概念汇率
    报告论文:是学生都copy下来,现在不用,将来绝对要用(转)
    情感日记:毕业临走物语
    美元为什么坚挺
    英特尔首席技术官:人机智能鸿沟将于2050年消失
  • 原文地址:https://www.cnblogs.com/downrainsun/p/11200405.html
Copyright © 2011-2022 走看看