• 线段树的lazy标记


    众所周知,当涉及到线段树的区间修改时,往往会引入lazy标记,通过懒惰标记的push_down优化区间修改和查询

    如这道板题:  题目链接

     1 #include <bits/stdc++.h>
     2 #define Lson(x) ((x)<<1)
     3 #define Rson(x) ((x)<<1|1)
     4 using namespace std;
     5 
     6 const int maxn =1e5;
     7 int d[(maxn<<2)+10];
     8 int lazy[(maxn<<2)+10];
     9 int raw[maxn+10];
    10 void build(int s,int t,int p){
    11     if(s==t){
    12         d[p] = raw[s];
    13         return;
    14     }
    15     int mid = (s+t)>>1;
    16     build(s,mid,Lson(p));
    17     build(mid+1,t,Rson(p));
    18     d[p] = d[Lson(p)] + d[Rson(p)];
    19     return;
    20 }
    21 
    22 void update(int L,int R,int C,int s,int t,int p){
    23     if(L==s&&t==R){
    24         d[p] = (R-L+1)*C;
    25         lazy[p] = C;
    26         return ;
    27     }
    28 
    29     int mid = (s+t)>>1;
    30 
    31     if(lazy[p]&&s!=t){//not leaf node and lazy
    32         d[Lson(p)] = (mid - s + 1 )*lazy[p];lazy[Lson(p)] = lazy[p];
    33         d[Rson(p)] = (t - mid)*lazy[p];lazy[Rson(p)] = lazy[p];
    34         lazy[p] = 0;
    35     }
    36 
    37     if(R<=mid) update(L,R,C,s,mid,Lson(p));
    38     else if(L>mid) update(L,R,C,mid+1,t,Rson(p));
    39     else update(L,mid,C,s,mid,Lson(p)) , update(mid+1,R,C,mid+1,t,Rson(p));
    40     d[p] = d[Lson(p)] + d[Rson(p)];
    41 }
    42 
    43 int query(int L,int R,int s,int t,int p){
    44     if(L==s&&t==R){
    45         return d[p];
    46     }
    47 
    48     int mid = (s+t)>>1;
    49     if(lazy[p]&&s!=t){
    50         d[Lson(p)] = (mid - s + 1 )*lazy[p];lazy[Lson(p)] = lazy[p];
    51         d[Rson(p)] = (t - mid)*lazy[p];lazy[Rson(p)] = lazy[p];
    52         lazy[p] = 0;
    53     }
    54 
    55     int sum = 0;
    56     if(R<=mid) sum+=query(L,R,s,mid,Lson(p));
    57     else if(L>mid) sum+=query(L,R,mid+1,t,Rson(p));
    58     else sum = query(L,mid,s,mid,Lson(p)) + query(mid+1,R,mid+1,t,Rson(p));
    59     return sum;
    60 }
    61 
    62 int main(){
    63     int N;
    64     scanf("%d",&N);
    65     for(int i=1;i<=N;++i){
    66         scanf("%d",&raw[i]);
    67     }
    68     build(1,N,1);
    69     int Q;
    70     scanf("%d",&Q);
    71     for (int i = 0; i < Q; ++i) {
    72         int op;
    73         scanf("%d",&op);
    74         if(op) {
    75             int L,R,C;
    76             scanf("%d%d%d",&L,&R,&C);
    77             update(L,R,C,1,N,1);
    78         } else {
    79             int L,R;
    80             scanf("%d%d",&L,&R);
    81             printf("%d
    ",query(L,R,1,N,1));
    82         }
    83     }
    84     return 0;
    85 }
    View Code

    而关于lazy标记,还有一种标签永久化的思路,还是一道板题:  题目链接

     这道题当然可以用正常的下推lazy标记写,但如果我们想标记永久化,减少下推过程也是可以做的。

    update部分搜到一点只改标签,然后改一路以来的 d[] 结点值(这点很重要,不然查询时,查到一个中间点可能结束,曾经对其后代的修改就丢失了)。query部分搜索时同时用ever记录一路以来的lazy标记值之和,ever其实就是应该增加却没有用过的值,搜到中间返回时计算区间和(d[]结点值,lazy标记与ever)。此题 lazy标记怎么加也不会爆int,但区间和可能会爆int。

     1 //#include <bits/stdc++.h>
     2 #include <cstdio>
     3 using namespace std;
     4 #define Lson(x) ((x)<<1)
     5 #define Rson(x) ((x)<<1|1)
     6 typedef long long ll;
     7 const int maxn = 1e5;
     8 ll d[(maxn<<2)+10];
     9 int lazy[(maxn<<2)+10];
    10 int raw[maxn+10];
    11 
    12 void build(int s,int t,int p){
    13     if(s==t){
    14         d[p] = 1ll*raw[s];
    15         lazy[p] = 0;
    16         return ;
    17     }
    18     int mid = (s+t) >> 1;
    19     build(s,mid,Lson(p));
    20     build(mid+1,t,Rson(p));
    21     lazy[p] = 0;
    22     d[p] = d[Lson(p)] + d[Rson(p)];
    23 }
    24 
    25 void update(int L,int R, int C, int s,int t,int p){
    26     if(L==s && t==R){
    27         lazy[p] += 1ll*C;
    28         return ;
    29     }
    30 
    31     d[p] += 1ll*(R-L+1)*C;//这一句保证当前点接受对其后代的修改
    32 
    33     int mid = (s+t) >> 1;
    34     if(R<=mid)
    35         update(L,R,C,s,mid,Lson(p));
    36     else if(L>mid)
    37         update(L,R,C,mid+1,t,Rson(p));
    38     else update(L,mid,C,s,mid,Lson(p)) , update(mid+1,R,C,mid+1,t,Rson(p));
    39 }
    40 
    41 ll query(int L,int R,int s,int t,int p,int ever){
    42     if(L==s && t==R){
    43         return 1ll*d[p] + 1ll*(t-s+1) * (lazy[p] + ever);
    44     }
    45     int mid = (s+t)>>1;
    46     ll sum = 0;
    47     if(R<=mid) sum += query(L,R,s,mid,Lson(p),ever + lazy[p]);
    48     else if(L>mid) sum += query(L,R,mid+1,t,Rson(p),ever + lazy[p]);
    49     else sum = query(L,mid,s,mid,Lson(p),ever + lazy[p]) + query(mid+1,R,mid+1,t,Rson(p),ever + lazy[p]);
    50     return sum;
    51 }
    52 
    53 int main(){
    54     int N,Q;
    55     while(~scanf("%d%d",&N,&Q)){
    56         for (int i = 1; i <= N; ++i) {
    57             scanf("%d",&raw[i]);
    58         }
    59         build(1,N,1);
    60         for (int i = 0; i < Q; ++i) {
    61             char op[3];
    62             scanf("%s",op);
    63             if(op[0] == 'Q'){
    64                 int a, b;
    65                 scanf("%d%d",&a,&b);
    66                 printf("%lld
    ",query(a,b,1,N,1,0));
    67             }
    68             else{
    69                 int a,b,c;
    70                 scanf("%d%d%d",&a,&b,&c);
    71                 update(a,b,c,1,N,1);
    72             }
    73         }
    74     }
    75     return 0;
    76 }
    View Code
    d[p] += 1ll*(R-L+1)*C;//这一句保证当前点接受对其后代的修改

    开始少了这句相当于丢掉了许多修改,因为query是会搜到一半就结束的,没有pushdown和maitain就需要修改一路以来的值。

    关于标签永久化,再请看这题:  题目链接

    题意是一个长为 n 的序列 a[n](下标1~n)  ,初始时每个元素为0,现在有m次操作L , R ,V将区间[L, R]里小于V的元素更新为V,最后询问每个 ai * i 的异或和。这道题的输入是根据三个初始数字和一个函数不断生成的,可以看做是随机的区间修改。(n<=1e5,m<=5e6)

    “区间[L, R]里小于V的元素更新为V”那肯定是有的元素更新有的不更新,而最后的询问是每个元素的值都用上了,值得注意的是只有一个询问。

    于是我们可以采用lazy标记永久化的思路,每一次区间修改中没有标记的push_down也没有maintain,只是单纯地更新区间lazy标记(如此线段树只需要存lazy标记),而在最后的查询里DFS搜到底部的过程中,我们只需要记录一路走到该点的最大标记值,然后计算对答案贡献即可。之前打标记复杂度是O(mlogn) 最后DFS遍历所有点O(nlogn)。

     1 #include <bits/stdc++.h>
     2 #define Lson(x) ((x)<<1)
     3 #define Rson(x) ((x)<<1|1)
     4 using namespace std;
     5 
     6 const int maxn = 1e5;
     7 const unsigned int mod = 1<<30;
     8 //const pair<int ,int > KONG = make_pair(0,0);
     9 unsigned X,Y,Z;
    10 //int d[(maxn<<2)+10];
    11 int lazy[(maxn<<2)+10];
    12 long long ans;
    13 unsigned int RNG61(){
    14     X = X^(X<<11);
    15     X = X^(X>>4);
    16     X = X^(X<<5);
    17     X = X^(X>>14);
    18     unsigned W = X ^(Y ^ Z);
    19     X = Y;
    20     Y = Z;
    21     Z = W;
    22     return Z;
    23 }
    24 
    25 void build(int s,int t,int p){
    26     if(s==t){
    27 //        d[p] = 0;
    28         lazy[p] = 0;
    29         return ;
    30     }
    31     int mid = (s+t)>>1;
    32     build(s,mid,Lson(p));
    33     build(mid+1,t,Rson(p));
    34 //    d[p] = 0;
    35     lazy[p] = 0;
    36     return;
    37 }
    38 
    39 void update(int L,int R,int C,int s,int t,int p){
    40     if(C < lazy[p]) return;//根本不用往下搜,剪枝
    41 
    42     if(L==s && R==t){
    43         lazy[p] = max(lazy[p],C);
    44         return ;
    45     }
    46     int mid = (s+t) >> 1;
    47     if(R<=mid) update(L,R,C,s,mid,Lson(p));
    48     else if(L>mid) update(L,R,C,mid+1,t,Rson(p));
    49     else update(L,mid,C,s,mid,Lson(p)),update(mid+1,R,C,mid+1,t,Rson(p));
    50 }
    51 
    52 
    53 void DFS(int s,int t,int p,int tmax){
    54    tmax = max(lazy[p],tmax);
    55    if(s==t){
    56 //       printf("%d :%d
    ",s,tmax);
    57        ans ^= 1ll*s*tmax;
    58        return;
    59    }
    60    int mid = (s+t)>>1;
    61    DFS(s,mid,Lson(p),tmax);
    62    DFS(mid+1,t,Rson(p),tmax);
    63    return ;
    64 }
    65 
    66 int main(){
    67 //    __clock_t stt = clock();
    68     int T;
    69     cin >> T;
    70     while(T--){
    71         int N,M;
    72         cin >> N >> M >> X >> Y >> Z;
    73         unsigned int n = static_cast<unsigned int>(N);
    74         build(1,N,1);
    75         for(int i=1;i<=M;++i){
    76             unsigned int f1 = RNG61();
    77             unsigned int f2 = RNG61();
    78             unsigned int f3 = RNG61();
    79             int L = (f1 % n) + 1;
    80             int R = (f2 % n) + 1;
    81             int V = f3 % (1<<30);
    82             update(min(L,R),max(L,R),V,1,N,1);
    83         }
    84         ans = 0;
    85         DFS(1,N,1,0);
    86         printf("%lld
    ",ans);
    87     }
    88 //    __clock_t ent = clock();
    89 //    printf("Used Time: %.3lf", static_cast<double>(ent - stt)/1000.0);
    90     return 0;
    91 }
    View Code

    开始写的还是TLE了,自己测了测时间大概20秒,DFS遍历所有点是必须的,那么其实可以在打标记时进行剪枝优化:

    if(C < lazy[p]) return;//根本不用往下搜,剪枝

    当前区间的lazy标记值已经大于修改值C,那么这个lazy标记将会覆盖C的影响,所以没有必要往下方更新。

    最后2秒通过,随机数据还是能剪枝不少。

  • 相关阅读:
    javascript中的throttle和debounce
    移动端Click300毫秒点击延迟的来龙去脉(转)
    HTTP Keep-Alive详解[转]
    深入浅出requireJS-1
    判断网站是微信打开的
    2015腾讯和阿里前端实习生面试经
    解决ant design 中 select的option 随页面滚动条滚动的bug
    解决window.close()方法兼容各个浏览器(ie关闭会进行弹框提示是否关闭)
    多维数组遍历添加新属性,以及获取所有key
    Vue 路由解耦与快捷新增
  • 原文地址:https://www.cnblogs.com/Kiritsugu/p/11455603.html
Copyright © 2020-2023  润新知