• HDU-4578 Transformation(线段树的多种区间操作)


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

    Time Limit: 15000/8000 MS (Java/Others)    Memory Limit: 65535/65536 K (Java/Others)

    Problem Description

    Yuanfang is puzzled with the question below: 
    There are n integers, a1, a2, …, an. The initial values of them are 0. There are four kinds of operations.
    Operation 1: Add c to each number between ax and ay inclusive. In other words, do transformation ak<---ak+c, k = x,x+1,…,y.
    Operation 2: Multiply c to each number between ax and ay inclusive. In other words, do transformation ak<---ak×c, k = x,x+1,…,y.
    Operation 3: Change the numbers between ax and ay to c, inclusive. In other words, do transformation ak<---c, k = x,x+1,…,y.
    Operation 4: Get the sum of p power among the numbers between ax and ay inclusive. In other words, get the result of axp+ax+1p+…+ay p.
    Yuanfang has no idea of how to do it. So he wants to ask you to help him. 

    Input

    There are no more than 10 test cases.
    For each case, the first line contains two numbers n and m, meaning that there are n integers and m operations. 1 <= n, m <= 100,000.
    Each the following m lines contains an operation. Operation 1 to 3 is in this format: "1 x y c" or "2 x y c" or "3 x y c". Operation 4 is in this format: "4 x y p". (1 <= x <= y <= n, 1 <= c <= 10,000, 1 <= p <= 3)
    The input ends with 0 0.

    Output

    For each operation 4, output a single integer in one line representing the result. The answer may be quite large. You just need to calculate the remainder of the answer when divided by 10007.

    Sample Input

    5 5
    3 3 5 7
    1 2 4 4
    4 1 5 2
    2 2 5 8
    4 3 5 3
    0 0

    Sample Output

    307
    7489

    Source

     
     
     

    题意:

    给你一个数组,初始值为零,有四种操作:

    (1)"1 x y c",代表 把区间 [x,y] 上的值全部加c

    (2)"2 x y c",代表 把区间 [x,y] 上的值全部乘以c

    (3)"3 x y c" 代表 把区间 [x,y]上的值全部赋值为c

    (4)"4 x y p" 代表 求区间 [x,y] 上值的p次方和1<=p<=3

    典型的线段树区间更新,不过是增加了多种操作。
    多种操作时,更新的时候各个懒惰标记之间更新顺序是有讲究的,而且各个懒惰标记之间往下推的时候也是能够互相影响。
    这道题思路简单,但是挺复杂的,很考验编码能力。

    注意:当线段树有多个懒惰标记时,一定要考虑到懒惰标记之间的互相影响。

    解法一:

    使用懒惰标记标记的是整个区间是否为同一个状态

    并且每次更新的时候回溯状态 即如果左右子结点都是相同状态 则在线段树的父节点上更新标记这一统一状态

    如此一来,只需要在计算区间的时候先判断区间内状态是否相同,然后进行区间内的更新操作即可 因为区间内都为统一状态。

    写法一(2433MS、5500k):

      1 #include <stdio.h>
      2 #include <string.h>
      3 #include <iostream>
      4 #include <string>
      5 #include <math.h>
      6 #include <algorithm>
      7 #include <vector>
      8 #include <queue>
      9 #include <set>
     10 #include <stack>
     11 #include <map>
     12 #include <math.h>
     13 const int INF=0x3f3f3f3f;
     14 typedef long long LL;
     15 const int mod=1e4+7;
     16 const int maxn=1e5+10;
     17 using namespace std;
     18 
     19 struct SegTree_node
     20 {
     21     int l;
     22     int r;
     23     int lazy;//懒惰标记 但是标记的是整个区间是否为同一个状态
     24     int num;
     25 }SegTree[maxn<<2];
     26 
     27 int n,m;
     28 
     29 void Build(int l,int r,int rt)
     30 {
     31     SegTree[rt].l=l;
     32     SegTree[rt].r=r;
     33     SegTree[rt].lazy=1;
     34     SegTree[rt].num=0;
     35     if(l==r)
     36         return ;
     37     int mid=(l+r)>>1;
     38     Build(l,mid,rt<<1);
     39     Build(mid+1,r,rt<<1|1);
     40 }
     41 
     42 void PushUp(int rt)
     43 {
     44     if(SegTree[rt<<1].lazy&&SegTree[rt<<1|1].lazy&&SegTree[rt<<1].num==SegTree[rt<<1|1].num)
     45     {//可以下放,因为我们每次都是先递归下去更改的子节点的值,所以在更改后将子节点的值赋给父节点。
     46         SegTree[rt].lazy=1;
     47         SegTree[rt].num=SegTree[rt<<1].num;
     48     }
     49     else//子节点的懒标记不同时为0或子节点值都不一样,肯定区间值不一样
     50         SegTree[rt].lazy=0; 
     51 }
     52 
     53 void PushDown(int rt)//下放懒标记
     54 {
     55     if(SegTree[rt].l==SegTree[rt].r)
     56         return ;
     57     if(SegTree[rt].lazy)
     58     {
     59         SegTree[rt<<1].lazy=SegTree[rt<<1|1].lazy=1;
     60         SegTree[rt<<1].num=SegTree[rt<<1|1].num=SegTree[rt].num;
     61         SegTree[rt].lazy=0;
     62     }
     63 }
     64 
     65 void Update(int L,int R,int C,int op,int rt)
     66 {
     67     int l=SegTree[rt].l;
     68     int r=SegTree[rt].r;
     69     if(L<=l&&R>=r&&SegTree[rt].lazy)//这里要注意,一定要保证现在区间值都一样才能修改。
     70     {
     71         if(op==1)
     72             SegTree[rt].num=(SegTree[rt].num+C)%mod;
     73         else if(op==2)
     74             SegTree[rt].num=(SegTree[rt].num*C)%mod;
     75         else
     76             SegTree[rt].num=C;
     77         return ;
     78     }
     79     PushDown(rt);
     80     int mid=(l+r)>>1;
     81     if(L<=mid)
     82         Update(L,R,C,op,rt<<1);
     83     if(R>mid)
     84         Update(L,R,C,op,rt<<1|1);
     85     PushUp(rt);
     86 }
     87 
     88 int Query(int L,int R,int P,int rt)
     89 {
     90     int l=SegTree[rt].l;
     91     int r=SegTree[rt].r;
     92     if(L<=l&&R>=r&&SegTree[rt].lazy)//要保证区间值一样才满足sum=(r-l+1)*a[rt]^p;这个式子
     93     {
     94         int ans=1;
     95         for(int i=1;i<=P;i++)
     96             ans=(ans*SegTree[rt].num)%mod;
     97         ans=(ans*(r-l+1))%mod;
     98         return ans;
     99     }
    100     PushDown(rt);
    101     int ans=0;
    102     int mid=(l+r)>>1;
    103     if(L<=mid)
    104         ans+=Query(L,R,P,rt<<1);
    105     if(R>mid)
    106         ans+=Query(L,R,P,rt<<1|1);
    107     return ans%mod;
    108 }
    109 
    110 int main()
    111 {
    112     while(~scanf("%d %d",&n,&m)&&(n||m))
    113     {
    114         Build(1,n,1);
    115         for(int i=1;i<=m;i++)
    116         {
    117             int op,x,y,c;
    118             scanf("%d %d %d %d",&op,&x,&y,&c);
    119             if(op>=1&&op<=3)//更新操作 
    120                 Update(x,y,c,op,1);
    121             else if(op==4)//查询操作 
    122             {
    123                 printf("%d
    ",Query(x,y,c,1));
    124             }
    125         }
    126     }
    127     return 0;
    128 }

    写法一的改进(2137MS、5496k):

    只有在二分子区间时写法不同

      1 #include <stdio.h>
      2 #include <string.h>
      3 #include <iostream>
      4 #include <string>
      5 #include <math.h>
      6 #include <algorithm>
      7 #include <vector>
      8 #include <queue>
      9 #include <set>
     10 #include <stack>
     11 #include <map>
     12 #include <math.h>
     13 const int INF=0x3f3f3f3f;
     14 typedef long long LL;
     15 const int mod=1e4+7;
     16 //const int mod=1e9+7;
     17 //const double PI=acos(-1);
     18 const int maxn=1e5+10;
     19 using namespace std;
     20 //ios::sync_with_stdio(false);
     21 //    cin.tie(NULL);
     22 
     23 struct SegTree_node
     24 {
     25     int l;
     26     int r;
     27     int lazy;//懒惰标记 但是标记的是整个区间是否为同一个状态
     28     int num;
     29 }SegTree[maxn<<2];
     30 
     31 int n,m,ans;
     32 
     33 void Build(int l,int r,int rt)
     34 {
     35     SegTree[rt].l=l;
     36     SegTree[rt].r=r;
     37     SegTree[rt].lazy=1;
     38     SegTree[rt].num=0;
     39     if(l==r)
     40         return ;
     41     int mid=(l+r)>>1;
     42     Build(l,mid,rt<<1);
     43     Build(mid+1,r,rt<<1|1);
     44 }
     45 
     46 void PushUp(int rt)
     47 {
     48     if(SegTree[rt<<1].lazy&&SegTree[rt<<1|1].lazy&&SegTree[rt<<1].num==SegTree[rt<<1|1].num)
     49     {//可以下放,因为我们每次都是先递归下去更改的子节点的值,所以在更改后将子节点的值赋给父节点。
     50         SegTree[rt].lazy=1;
     51         SegTree[rt].num=SegTree[rt<<1].num;
     52     }
     53     else//子节点的懒标记不同时为0或子节点值都不一样,肯定区间值不一样
     54         SegTree[rt].lazy=0; 
     55 }
     56 
     57 void PushDown(int rt)
     58 {
     59     if(SegTree[rt].l==SegTree[rt].r)
     60         return ;
     61     if(SegTree[rt].lazy)
     62     {
     63         SegTree[rt<<1].lazy=SegTree[rt<<1|1].lazy=1;
     64         SegTree[rt<<1].num=SegTree[rt<<1|1].num=SegTree[rt].num;
     65         SegTree[rt].lazy=0;
     66     }
     67 }
     68 
     69 void Update(int L,int R,int C,int op,int rt)
     70 {
     71     int l=SegTree[rt].l;
     72     int r=SegTree[rt].r;
     73     if(L==l&&R==r&&SegTree[rt].lazy)//这里要注意,一定要保证现在区间值都一样才能修改。
     74     {
     75         if(op==1)
     76             SegTree[rt].num=(SegTree[rt].num+C)%mod;
     77         else if(op==2)
     78             SegTree[rt].num=(SegTree[rt].num*C)%mod;
     79         else
     80             SegTree[rt].num=C;
     81         return ;
     82     }
     83     PushDown(rt);
     84     int mid=(l+r)>>1;
     85     if(R<=mid)
     86         Update(L,R,C,op,rt<<1);
     87     else if(L>mid)
     88         Update(L,R,C,op,rt<<1|1);
     89     else
     90     {
     91         Update(L,mid,C,op,rt<<1);
     92         Update(mid+1,R,C,op,rt<<1|1);
     93     }
     94     PushUp(rt);
     95 }
     96 
     97 void Query(int L,int R,int P,int rt)
     98 {
     99     int l=SegTree[rt].l;
    100     int r=SegTree[rt].r;
    101     if(L==l&&R==r&&SegTree[rt].lazy)//要保证区间值一样才满足sum=(r-l+1)*a[rt]^p;这个式子
    102     {
    103         int tem=1;
    104         for(int i=1;i<=P;i++)
    105             tem=(tem*SegTree[rt].num)%mod;
    106         tem=(tem*(r-l+1))%mod;
    107         ans=(ans+tem)%mod;
    108         return ;
    109     }    
    110     PushDown(rt);
    111     int mid=(l+r)>>1;
    112     if(R<=mid)
    113         Query(L,R,P,rt<<1);
    114     else if(L>mid)
    115         Query(L,R,P,rt<<1|1);
    116     else
    117     {
    118         Query(L,mid,P,rt<<1);
    119         Query(mid+1,R,P,rt<<1|1);
    120     }
    121 }
    122 
    123 int main()
    124 {
    125     while(~scanf("%d %d",&n,&m)&&(n||m))
    126     {
    127         Build(1,n,1);
    128         ans=0;
    129         for(int i=1;i<=m;i++)
    130         {
    131             int op,x,y,c;
    132             scanf("%d %d %d %d",&op,&x,&y,&c);
    133             if(op>=1&&op<=3)//更新操作 
    134                 Update(x,y,c,op,1);
    135             else if(op==4)//查询操作 
    136             {
    137                 ans=0;
    138                 Query(x,y,c,1);
    139                 printf("%d
    ",ans);
    140             }
    141         }
    142     }
    143     return 0;
    144 } 

    解法二:

    用线段树维护里面的值都相等的区间 ,那么这个区间的所需答案为(r-l+1)*(tree[v].eq)^q

    用add表示加号标记,mul表示乘,eq表示等,无疑在向下更新的时候等号的优先级应该最高,先处理等号标记,并把加号和乘号标记置为0和1(初始值),然后重点来了,在处理乘号标记的时候,如果发现子区间有等号标记,直接修改等号标记,否则先将子区间pushdwon一下清空标记,再进行该子区间标记,加号同理,如果子区间有等号标记,就修改等号标记,否则将子区间pushdown清空标记,再将该子区间标记,所有操作完都要清空当前区间标记,然后修改的时候也差不多,先检查该区间有没有等号标记,有的话直接修改等号标记,否则pushdown清空该区间标记,再进行各种标记,查询的时候是整个算法的精髓,他是查询到要查询区间的子区间的等号标记不为初始标记,即为一段相同的数字的时候停止,然后返回该区间的值,进行各个子区间累加,得出答案。


    写法二(2574MS、6516k):
      1 #include <stdio.h>
      2 #include <string.h>
      3 #include <iostream>
      4 #include <string>
      5 #include <math.h>
      6 #include <algorithm>
      7 #include <vector>
      8 #include <queue>
      9 #include <set>
     10 #include <stack>
     11 #include <map>
     12 #include <math.h>
     13 const int INF=0x3f3f3f3f;
     14 typedef long long LL;
     15 const int mod=1e4+7;
     16 const int maxn=1e5+10;
     17 using namespace std;
     18 
     19 struct SegTree_node
     20 {
     21     int l;
     22     int r;
     23     int add;//表示加号标记
     24     int mul;//表示乘号标记
     25     int eq; //表示等号标记
     26 }SegTree[maxn<<2];
     27 
     28 int n,m;
     29 
     30 void Build(int l,int r,int rt)
     31 {
     32     SegTree[rt].l=l;
     33     SegTree[rt].r=r;
     34     SegTree[rt].add=0;
     35     SegTree[rt].mul=1;
     36     SegTree[rt].eq=-1;
     37     if(l==r)
     38     {
     39         SegTree[rt].eq=0;//最底层要赋值为0
     40         return ;
     41     }
     42     int mid=(l+r)>>1;
     43     Build(l,mid,rt<<1);
     44     Build(mid+1,r,rt<<1|1);
     45 }
     46 
     47 void PushDown(int rt)
     48 {
     49     int l=SegTree[rt].l;
     50     int r=SegTree[rt].r;
     51     if(l==r)//没有子区间了不用向下更新了
     52         return ;
     53     if(SegTree[rt].eq!=-1)//处理等号
     54     {
     55         SegTree[rt<<1].eq=SegTree[rt<<1|1].eq=SegTree[rt].eq;//更新子区间等号标记
     56         SegTree[rt<<1].add=SegTree[rt<<1|1].add=0;//还原子区间乘法标记
     57         SegTree[rt<<1].mul=SegTree[rt<<1|1].mul=1;//清空子区间加乘标记
     58         SegTree[rt].eq=-1;//记得还原
     59         return ;
     60     }
     61     if(SegTree[rt].mul!=1)//处理乘号
     62     {
     63         if(SegTree[rt<<1].eq!=-1)//如果子区间有等号标记,直接修改等号标记
     64             SegTree[rt<<1].eq=(SegTree[rt<<1].eq*SegTree[rt].mul)%mod;
     65         else//否则清空该子区间标记,进行子区间标记
     66         {
     67             PushDown(rt<<1);
     68             SegTree[rt<<1].mul=(SegTree[rt<<1].mul*SegTree[rt].mul)%mod;
     69         }
     70         if(SegTree[rt<<1|1].eq!=-1)//同理处理右区间
     71             SegTree[rt<<1|1].eq=(SegTree[rt<<1|1].eq*SegTree[rt].mul)%mod;
     72         else
     73         {
     74             PushDown(rt<<1|1);
     75             SegTree[rt<<1|1].mul=(SegTree[rt<<1|1].mul*SegTree[rt].mul)%mod;
     76         }
     77         SegTree[rt].mul=1;//记得还原
     78     }
     79     if(SegTree[rt].add)//处理加号标记,和上面同理处理
     80     {
     81         if(SegTree[rt<<1].eq!=-1)
     82             SegTree[rt<<1].eq=(SegTree[rt<<1].eq+SegTree[rt].add)%mod;
     83         else
     84         {
     85             PushDown(rt<<1);
     86             SegTree[rt<<1].add=(SegTree[rt<<1].add+SegTree[rt].add)%mod;
     87         }
     88         if(SegTree[rt<<1|1].eq!=-1)
     89             SegTree[rt<<1|1].eq=(SegTree[rt<<1|1].eq+SegTree[rt].add)%mod;
     90         else
     91         {
     92             PushDown(rt<<1|1);
     93             SegTree[rt<<1|1].add=(SegTree[rt<<1|1].add+SegTree[rt].add)%mod;
     94         }
     95         SegTree[rt].add=0;//记得还原
     96     }
     97 }
     98 
     99 void Update(int L,int R,int C,int op,int rt)
    100 {
    101     int l=SegTree[rt].l;
    102     int r=SegTree[rt].r;
    103     if(L<=l&&R>=r)
    104     {
    105         if(op==3)
    106         {
    107             SegTree[rt].add=0;
    108             SegTree[rt].mul=1;
    109             SegTree[rt].eq=C;
    110             return ;
    111         }
    112         if(SegTree[rt].eq!=-1)//如果有等号标记,就直接修改等号标记
    113         {
    114             if(op==1)
    115                 SegTree[rt].eq=(SegTree[rt].eq+C)%mod;
    116             else if(op==2)
    117                 SegTree[rt].eq=(SegTree[rt].eq*C)%mod;
    118         }
    119         else
    120         {
    121             PushDown(rt);//否则清空该区间,进行标记
    122             if(op==1)
    123                 SegTree[rt].add=(SegTree[rt].add+C)%mod;
    124             else if(op==2)
    125                 SegTree[rt].mul=(SegTree[rt].mul*C)%mod;
    126         }
    127         return ;
    128     }
    129     PushDown(rt);//向下传递状态
    130     int mid=(l+r)>>1;
    131     if(L<=mid)
    132         Update(L,R,C,op,rt<<1);
    133     if(R>mid)
    134         Update(L,R,C,op,rt<<1|1);
    135 }
    136 
    137 int Query(int L,int R,int P,int rt)//查询
    138 {
    139     int l=SegTree[rt].l;
    140     int r=SegTree[rt].r;
    141     if(L<=l&&R>=r&&SegTree[rt].eq!=-1)//查到是查询区间的子区间且一段全为相同的数
    142     {
    143         int ans=1;
    144         for(int i=1;i<=P;i++)
    145             ans=(ans*SegTree[rt].eq)%mod;
    146         return (ans*(r-l+1)) %mod;//注意要乘上长度
    147     }
    148     PushDown(rt);
    149     int mid=(l+r)>>1;
    150     if(R<=mid)
    151         return Query(L,R,P,rt<<1);
    152     else if(L>mid)
    153         return Query(L,R,P,rt<<1|1);
    154     else
    155         return (Query(L,mid,P,rt<<1)+Query(mid+1,R,P,rt<<1|1))%mod;
    156 }
    157 
    158 int main()
    159 {
    160     while(~scanf("%d %d",&n,&m)&&(n||m))
    161     {
    162         Build(1,n,1);
    163         for(int i=1;i<=m;i++)
    164         {
    165             int op,x,y,c;
    166             scanf("%d %d %d %d",&op,&x,&y,&c);
    167             if(op>=1&&op<=3)//更新操作 
    168                 Update(x,y,c,op,1);
    169             else if(op==4)//查询操作 
    170                 printf("%d
    ",Query(x,y,c,1)%mod);
    171         }
    172     }
    173     return 0;
    174 }
     
     
     
     
     
     
     
     
     
     
  • 相关阅读:
    若干代码坏味及解法
    编程漫谈(十八):编程三境界
    如何不虚度光阴
    打印预览内嵌浏览器的两种方法
    LODOP多个表格带表格页脚关联
    Akka学习笔记
    Spring和Springboot相关知识点整理
    python接口测试:在一个用例文件中调用另一个用例文件中定义的方法
    使用jmeter对字符串进行加密
    (八十九)c#Winform自定义控件-自定义滚动条(treeview、panel、datagridview、listbox、listview、textbox)
  • 原文地址:https://www.cnblogs.com/jiamian/p/11421697.html
Copyright © 2020-2023  润新知