• 初涉主席树


    高贵冷艳的主席树……

    主席树是什么

    是一种数据结构

    主席树对于序列$1..i$的每一个前缀各建一颗值域线段树。

    很重要的是主席树具有可减性,这是它能够区间操作的重要前提,这有点类似于前缀和的思想。

    好的假设我们现在建出了这$n$颗线段树,那么我们的确是可以进行各种区间操作了。但是每颗线段树的空间是$maxn<<4$啊,空间复杂度显然要出事。

    回到最初主席树的实现上来:1.它对于前缀建树;2.它建的是值域线段树。

    性质一:对于前缀建树

    那么相邻之间的两颗树的重复信息是很多的嘛,每次修改$logn$的节点信息,于是我们可以共享其余相同的节点。

    性质二:建的是值域线段树

    显然值域线段树的构造形态是和所处位置无关的,换句话说就是这$n$颗值域线段树的构造是相同的,只是其节点的$val$不一样。这一点保证了主席树共享节点后的空间复杂度的正确性。

    至于如何共享节点,其实也没有什么刁钻的操作。我们只要动态开点,并且继承上一次的节点信息就好了。

    以上是静态主席树的大致内容,挂一篇好博客:题解 P3834 【【模板】可持久化线段树 1(主席树)】

    主席树的难点实际在于应用而不是其理解(怎么和网络流一样)。

    例题

    P3834 【模板】可持久化线段树 1(主席树)

    题目描述

    如题,给定N个正整数构成的序列,将对于指定的闭区间查询其区间内的第K小值。

    输入输出格式

    输入格式:

    第一行包含两个正整数N、M,分别表示序列的长度和查询的个数。

    第二行包含N个正整数,表示这个序列各项的数字。

    接下来M行每行包含三个整数 l, r, kl,r,k , 表示查询区间 [l, r][l,r] 内的第k小值。

    输出格式:

    输出包含k行,每行1个正整数,依次表示每一次查询的结果


    题目分析

    挂代码:

     1 #include<bits/stdc++.h>
     2 const int maxn = 200035;
     3 
     4 struct node
     5 {
     6     int l,r,val,root;
     7 }a[maxn<<5];
     8 int tot,n,m,q;
     9 int x[maxn],b[maxn];
    10 
    11 int read()
    12 {
    13     char ch = getchar();
    14     int num = 0;
    15     bool fl = 0;
    16     for (; !isdigit(ch); ch = getchar())
    17         if (ch=='-') fl = 1;
    18     for (; isdigit(ch); ch = getchar())
    19         num = (num<<1)+(num<<3)+ch-48;
    20     if (fl) num = -num;
    21     return num;
    22 }
    23 int build(int l, int r)
    24 {
    25     if (l==r) return ++tot;
    26     int rt = ++tot, mid = (l+r)>>1;
    27     a[rt].l = build(l, mid);
    28     a[rt].r = build(mid+1, r);
    29     return rt;
    30 }
    31 int update(int pre, int l, int r, int x)
    32 {
    33     int rt = ++tot, mid = (l+r)>>1;
    34     a[rt] = a[pre];
    35     a[rt].val++;
    36     if (l < r){
    37         if (x <= mid) a[rt].l = update(a[pre].l, l, mid, x);
    38         else a[rt].r = update(a[pre].r, mid+1, r, x);
    39     }
    40     return rt;
    41 }
    42 int query(int L, int R, int l, int r, int k)
    43 {
    44     if (l==r) return l;
    45     int val = a[a[R].l].val-a[a[L].l].val, mid = (l+r)>>1;
    46     if (k <= val) return query(a[L].l, a[R].l, l, mid, k);
    47     return query(a[L].r, a[R].r, mid+1, r, k-val);
    48 } 
    49 int main()
    50 {
    51     n = read(), q = read();
    52     for (int i=1; i<=n; i++) x[i] = b[i] = read();
    53     std::sort(b+1, b+n+1);
    54     m = std::unique(b+1, b+n+1)-b-1;
    55     a[0].root = build(1, m);
    56     for (int i=1; i<=n; i++)
    57     {
    58         int tt = std::lower_bound(b+1, b+m+1, x[i])-b;
    59         a[i].root = update(a[i-1].root, 1, m, tt);
    60     }
    61     for (int i=1; i<=q; i++)
    62     {
    63         int l = read(), r = read(), k = read();
    64         printf("%d
    ",b[query(a[l-1].root, a[r].root, 1, m, k)]);
    65     }
    66     return 0;
    67 }

    「二分」#2555. 「CTSC2018」混合果汁

    题目描述

    小 R 热衷于做黑暗料理,尤其是混合果汁。

    商店里有 nnn 种果汁,编号为 0,1,2,...,n1。iii 号果汁的美味度是 did_idi​​,每升价格为 pip_ipi​​。小 R 在制作混合果汁时,还有一些特殊的规定,即在一瓶混合果汁中,iii 号果汁最多只能添加 lil_ili​​ 升。

    现在有 mmm 个小朋友过来找小 R 要混合果汁喝,他们都希望小 R 用商店里的果汁制作成一瓶混合果汁。其中,第 jjj 个小朋友希望他得到的混合果汁总价格不大于 gjg_jgj​​,体积不小于 LjL_jLj​​。在上述这些限制条件下,小朋友们还希望混合果汁的美味度尽可能地高,一瓶混合果汁的美味度等于所有参与混合的果汁的美味度的最小值。请你计算每个小朋友能喝到的最美味的混合果汁的美味度。

    输入格式

    输入第一行包含两个正整数 n,mn, mn,m,表示果汁的种数和小朋友的数量。接下来 nnn 行,每行三个正整数 di,pi,lid_i, p_i, l_idi​​,pi​​,li​​,表示 iii 号果汁的美味度为 did_idi​​,每升价格为pip_ipi​​,在一瓶果汁中的添加上限为 lil_ili​​。

    接下来 mmm 行依次描述所有小朋友:每行两个数正整数 gj,Ljg_j, L_jgj​​,Lj​​ 描述一个小朋友,表示他最多能支付 gjg_jgj​​ 元钱,他想要至少 LjL_jLj​​ 升果汁。

    输出格式

    对于所有小朋友依次输出:对于每个小朋友,输出一行,包含一个整数,表示他能喝到的最美味的混合果汁的美味度。如果无法满足他的需求,则输出 1。

    数据范围与提示

    对于所有的测试数据,保证n,m≤100000,1≤di,pi,li≤105,1≤gj,Lj≤10^18。


    题目分析

    主席树上二分

    一般来说呢,这些有关数列的值的操作总是可以用主席树维护的。然后这题求的是最小值最大化,于是二分判断就好啦。

    %%%jyz代码又简洁又跑得快

     1 #include<bits/stdc++.h>
     2 typedef long long ll;
     3 const int maxn = 100035;
     4 
     5 struct node
     6 {
     7     int d,p,l;
     8     bool operator < (node a) const
     9     {
    10         return d<a.d;
    11     }
    12 }a[maxn];
    13 int n,m,mx;
    14 int rt[maxn],ls[maxn<<7],rs[maxn<<7],tot;
    15 ll num[maxn<<7],sum[maxn<<7];
    16 
    17 ll read()
    18 {
    19     ll num = 0;
    20     char ch = getchar();
    21     bool fl = 0;
    22     for (; !isdigit(ch); ch = getchar())
    23         if (ch=='-') fl = 1;
    24     for (; isdigit(ch); ch = getchar())
    25         num = (num<<1)+(num<<3)+ch-48;
    26     if (fl) num = -num;
    27     return num;
    28 }
    29 int update(int pre, int l, int r, int u, int v)
    30 {
    31     int nd = ++tot, mid = (l+r)>>1;
    32     ls[nd] = ls[pre], rs[nd] = rs[pre];
    33     sum[nd] = sum[pre]+1ll*u*v;
    34     num[nd] = num[pre]+v;
    35     if (l < r){
    36         if (u <= mid) ls[nd] = update(ls[pre], l, mid, u, v);
    37         else rs[nd] = update(rs[pre], mid+1, r, u, v);
    38     }
    39     return nd;
    40 }
    41 ll query(int nd, int l, int r, int k)
    42 {
    43     if (l==r) return 1ll*l*k;
    44     int mid = (l+r)>>1;
    45     if (k > num[ls[nd]]) return sum[ls[nd]]+query(rs[nd], mid+1, r, k-num[ls[nd]]);
    46     return query(ls[nd], l, mid, k);
    47 }
    48 int main()
    49 {
    50     n = read(), m = read();
    51     for (int i=1; i<=n; i++)
    52         a[i].d = read(), a[i].p = read(), a[i].l = read(),
    53         mx = a[i].p > mx?a[i].p:mx;
    54     std::sort(a+1, a+n+1);
    55     mx++;
    56     for (int i=n; i; i--)
    57         rt[i] = update(rt[i+1], 1, mx, a[i].p, a[i].l);
    58     for (int i=1; i<=m; i++)
    59     {
    60         ll g = read(), l = read();
    61         if (sum[rt[1]] < l||query(rt[1], 1, mx, l)>g){
    62             printf("-1
    ");
    63             continue;
    64         }
    65         int lf = 1, rf = n, mid;
    66         while (lf <= rf)
    67         {
    68             mid = (lf+rf)>>1;
    69             if (num[rt[mid]]>=l&&query(rt[mid], 1, mx, l)<=g)
    70                 lf = mid+1;
    71             else rf = mid-1;
    72         }
    73         printf("%d
    ",a[rf].d);
    74     }
    75     return 0;
    76 }

    「差分」3932: [CQOI2015]任务查询系统

    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行,每行一个整数,表示查询结果。

    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的一个排列

    题目分析

    我们可以用类似于差分的思想,把每条线段拆成两个端点,再按照优先级建一颗主席树。

    然后预处理一下每一个时间点应该查询哪一个端点,于是就好了。

     1 #include<bits/stdc++.h>
     2 typedef long long ll;
     3 const int maxNode = 200035;
     4 const int maxn = maxNode*40;
     5 
     6 struct node
     7 {
     8     ll pos,val;
     9     bool operator < (node a) const
    10     {
    11         return pos < a.pos;
    12     }
    13     node() {}
    14     node(ll a, ll b):pos(a),val(b) {}
    15 }a[maxNode];
    16 int n,m,cnt,tot;
    17 ll pre,mx,rt[maxNode],ls[maxn],rs[maxn];
    18 ll size[maxn],sum[maxn],to[maxNode];
    19 
    20 ll read()
    21 {
    22     ll num = 0;
    23     char ch = getchar();
    24     bool fl = 0;
    25     for (; !isdigit(ch); ch = getchar())
    26         if (ch=='-') fl = 1;
    27     for (; isdigit(ch); ch = getchar())
    28         num = (num<<1)+(num<<3)+ch-48;
    29     if (fl) num = -num;
    30     return num;
    31 }
    32 void pushup(int x)
    33 {
    34     size[x] = size[ls[x]]+size[rs[x]];
    35     sum[x] = sum[ls[x]]+sum[rs[x]];
    36 }
    37 ll update(ll pre, ll l, ll r, ll c)
    38 {
    39     ll nd = ++tot, mid = (l+r)>>1;
    40     ls[nd] = ls[pre], rs[nd] = rs[pre];
    41     if (l==r){
    42         if (c > 0)
    43             size[nd] = size[pre]+1;
    44         else size[nd] = size[pre]-1;
    45         sum[nd] = sum[pre]+c;
    46         return nd;
    47     }
    48     if (abs(c) <= mid)
    49         ls[nd] = update(ls[pre], l, mid, c);
    50     else rs[nd] = update(rs[pre], mid+1, r, c);
    51     pushup(nd);
    52     return nd;
    53 }
    54 ll check(ll x, ll k)
    55 {
    56     ll nd = rt[x], ret = 0, l = 1, r = mx, mid;
    57     if (size[nd] <= k) return sum[nd];
    58     while (l < r)
    59     {
    60         mid = (l+r)>>1;
    61         if (size[ls[nd]] >= k){
    62             r = mid;
    63             nd = ls[nd];
    64         }else{
    65             l = mid+1;
    66             ret += sum[ls[nd]];
    67             k -= size[ls[nd]];
    68             nd = rs[nd];
    69         }
    70     }
    71     return ret += l*k;
    72 }
    73 int main()
    74 {
    75     m = read(), n = read();
    76     for (int i=1; i<=m; i++)
    77     {
    78         ll u = read(), v = read(), w = read();
    79         a[++cnt] = node(u, w);
    80         a[++cnt] = node(v+1, -w);
    81         mx = mx < w+1?w+1:mx;
    82     }
    83     std::sort(a+1, a+cnt+1);
    84     for (int i=1; i<=cnt; i++)
    85         rt[i] = update(rt[i-1], 1, mx, a[i].val);
    86     for (int i=cnt; i; i--)
    87         if (a[i].pos!=a[i+1].pos)
    88             to[a[i].pos] = i;
    89     for (int i=1; i<=n; i++)
    90         if (!to[i]) to[i] = to[i-1];
    91     pre = 1;
    92     for (int i=1; i<=n; i++)
    93     {
    94         ll x = read(), a = read(), b = read(), c = read(), k = 1+(a*pre+b)%c;
    95         pre = check(to[x], k);
    96         printf("%lld
    ",pre);
    97     }
    98     return 0;
    99 }

    「套树状数组」bzoj1901: Zju2112 Dynamic Rankings

    Description

    给定一个含有n个数的序列a[1],a[2],a[3]……a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1
    ],a[i+2]……a[j]中第k小的数是多少(1≤k≤j-i+1),并且,你可以改变一些a[i]的值,改变后,程序还能针对改
    变后的a继续回答上面的问题。

    Input

    第一行有两个正整数n(1≤n≤10000),m(1≤m≤10000)。
    分别表示序列的长度和指令的个数。
    第二行有n个数,表示a[1],a[2]……a[n],这些数都小于10^9。
    接下来的m行描述每条指令
    每行的格式是下面两种格式中的一种。 
    Q i j k 或者 C i t 
    Q i j k (i,j,k是数字,1≤i≤j≤n, 1≤k≤j-i+1)
    表示询问指令,询问a[i],a[i+1]……a[j]中第k小的数。
    C i t (1≤i≤n,0≤t≤10^9)表示把a[i]改变成为t
    m,n≤10000

    Output

     对于每一次询问,你都需要输出他的答案,每一个输出占单独的一行。


    题目分析

    动态区间第k大的模板题(感动,bzoj居然还有板子题)

    我们在外层套树状数组就好了

    注意一下细节不要打挂(其实还是要多练练熟了就好了)

     1 #include<bits/stdc++.h>
     2 const int maxn = 10013;
     3 
     4 struct node
     5 {
     6     int i,j,k;
     7 }qr[maxn];
     8 int n,m;
     9 int v[maxn],col[maxn<<1],tot,cnt;
    10 int rt[maxn],ls[maxn<<8],rs[maxn<<8],val[maxn<<8];
    11 int cntl,cntr,L[31],R[31];
    12 char cmd[13];
    13 
    14 int read()
    15 {
    16     int num = 0;
    17     char ch = getchar();
    18     bool fl = 0;
    19     for (; !isdigit(ch); ch = getchar())
    20         if (ch=='-') fl = 1;
    21     for (; isdigit(ch); ch = getchar())
    22         num = (num<<1)+(num<<3)+ch-48;
    23     if (fl) num = -num;
    24     return num;
    25 }
    26 inline int lowbit(int x){return x&-x;}
    27 int update(int pre, int l, int r, int x, int c)
    28 {
    29     int nd = ++tot, mid = (l+r)>>1;
    30     ls[nd] = ls[pre], rs[nd] = rs[pre];
    31     val[nd] = val[pre]+c;
    32     if (l==r) return nd;
    33     if (x <= mid)
    34         ls[nd] = update(ls[pre], l, mid, x, c);
    35     else rs[nd] = update(rs[pre], mid+1, r, x, c);
    36     return nd;
    37 }
    38 int query(int l, int r, int k)
    39 {
    40     if (l==r) return l;
    41     int suml = 0, sumr = 0, mid = (l+r)>>1;
    42     for (int i=1; i<=cntl; i++) suml += val[ls[L[i]]];
    43     for (int i=1; i<=cntr; i++) sumr += val[ls[R[i]]];
    44     if (sumr-suml >= k){
    45         for (int i=1; i<=cntl; i++) L[i] = ls[L[i]];
    46         for (int i=1; i<=cntr; i++) R[i] = ls[R[i]];
    47         return query(l, mid, k);
    48     }else{
    49         for (int i=1; i<=cntl; i++) L[i] = rs[L[i]];
    50         for (int i=1; i<=cntr; i++) R[i] = rs[R[i]];
    51         return query(mid+1, r, k-(sumr-suml));
    52     }
    53 }
    54 int main()
    55 {
    56     n = read(), m = read();
    57     for (int i=1; i<=n; i++)
    58         v[i] = col[++cnt] = read();
    59     for (int i=1; i<=m; i++)
    60     {
    61         scanf("%s",cmd);
    62         qr[i].i = read(), qr[i].j = read();
    63         if (cmd[0]=='Q') qr[i].k = read();
    64         else col[++cnt] = qr[i].j;
    65     }
    66     std::sort(col+1, col+cnt+1);
    67     cnt = std::unique(col+1, col+cnt+1)-col-1;
    68     for (int i=1; i<=n; i++)
    69     {
    70         int tt = std::lower_bound(col+1, col+cnt+1, v[i])-col;
    71         for (int j=i; j<=n; j+=lowbit(j))
    72             rt[j] = update(rt[j], 1, cnt, tt, 1);
    73     }
    74     for (int i=1; i<=m; i++)
    75         if (qr[i].k){
    76             cntl = 0, cntr = 0;
    77             qr[i].i--;
    78             for (int j=qr[i].i; j; j-=lowbit(j)) L[++cntl] = rt[j];
    79             for (int j=qr[i].j; j; j-=lowbit(j)) R[++cntr] = rt[j];
    80             printf("%d
    ",col[query(1, cnt, qr[i].k)]);
    81         }else{
    82             int tt = std::lower_bound(col+1, col+cnt+1, v[qr[i].i])-col;
    83             for (int j=qr[i].i; j<=n; j+=lowbit(j))
    84                 rt[j] = update(rt[j], 1, cnt, tt, -1);
    85             v[qr[i].i] = qr[i].j;
    86             tt = std::lower_bound(col+1, col+cnt+1, v[qr[i].i])-col;
    87             for (int j=qr[i].i; j<=n; j+=lowbit(j))
    88                 rt[j] = update(rt[j], 1, cnt, tt, 1);
    89         }
    90     return 0;
    91 }

    END

  • 相关阅读:
    😉P03 Go 基础知识😉
    😎P03 DB 数据库的约束条件、表关系、修改表语法以及复制表😎
    😉P02 Go 快速上手😉
    C# NPOI导出Excel横向纵向显示
    C# 批量上传文件 添加图片水印
    C# 压缩ZIP
    SQL Server循环插入100000条数据
    C# 特殊字符过滤拦截
    C# 导入Excel到数据库
    C# 实现批量删除功能
  • 原文地址:https://www.cnblogs.com/antiquality/p/9156848.html
Copyright © 2020-2023  润新知