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
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
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
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\n",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\n",ans); 140 } 141 } 142 } 143 return 0; 144 }
解法二:
用线段树维护里面的值都相等的区间 ,那么这个区间的所需答案为(r-l+1)*(tree[v].eq)^q
用add表示加号标记,mul表示乘,eq表示等,无疑在向下更新的时候等号的优先级应该最高,先处理等号标记,并把加号和乘号标记置为0和1(初始值),然后重点来了,在处理乘号标记的时候,如果发现子区间有等号标记,直接修改等号标记,否则先将子区间pushdwon一下清空标记,再进行该子区间标记,加号同理,如果子区间有等号标记,就修改等号标记,否则将子区间pushdown清空标记,再将该子区间标记,所有操作完都要清空当前区间标记,然后修改的时候也差不多,先检查该区间有没有等号标记,有的话直接修改等号标记,否则pushdown清空该区间标记,再进行各种标记,查询的时候是整个算法的精髓,他是查询到要查询区间的子区间的等号标记不为初始标记,即为一段相同的数字的时候停止,然后返回该区间的值,进行各个子区间累加,得出答案。
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\n",Query(x,y,c,1)%mod); 171 } 172 } 173 return 0; 174 }