Addition and Multiplication
You are given a one dimensional integer array A of length N. You need to maintain the array under Qqueries of the following four types. Assume 1-based indexing and M = 109 + 7.
Query 1 : 1 x y v
This implies adding v to array A to all the indices from x to y, i.e.,
This implies adding v to array A to all the indices from x to y, i.e.,
for (i = x; i <= y; i++) Ai += v; Ai %= M;
Query 2 : 2 x y v
This implies multiplying the scalar v with the array A for all the indices from x to y, i.e.,
This implies multiplying the scalar v with the array A for all the indices from x to y, i.e.,
for (i = x; i <= y; i++)
Ai *= v
Ai %= M
Query 3 : 3 x y v
This implies initializing the array A at all the indices from x to y with the value v, i.e.,
This implies initializing the array A at all the indices from x to y with the value v, i.e.,
for (i = x; i <= y; i++) Ai = v
Query 4 : 4 x y
This is a report query which needs to find the sum of the values in A from x to y, i.e.,
This is a report query which needs to find the sum of the values in A from x to y, i.e.,
sum = 0; for (i = x; i <= y; i++) sum += Ai sum %= M Output sum.
Note: a%b represents the remainder of a when divided by b.
Input
- First line contains two space separated integers N, Q.
- Next line contains N space separated integers denoting the array A.
- Next Q lines contain queries which can be of any of the above four types.
Output
- For each query of type 4, output a single integer corresponding to the answer in a single line.
Constraints
- 1 ≤ N ≤ 105
- 1 ≤ Q ≤ 105
- 1 ≤ Initial value of Ai ≤ 109
- 1 ≤ v ≤ 109
Subtasks
- Subtask 1: Queries are of type 1, 3 and 4. Points - 5.
- Subtask 2: Queries are of type 2, 3 and 4. Points - 5.
- Subtask 3: Queries are of type 1, 2 and 4. Points - 30.
- Subtask 4: All the queries are present. Points - 60.
Example
Input: 4 4 1 2 3 4 4 1 4 1 1 3 10 2 2 4 2 4 1 4 Output: 10 69
Explanation
Initial A : [1, 2, 3, 4]
Result of first query : 1 + 2 + 3 + 4 = 10
A after second query: [ 11, 12, 13, 4]
A after third query : [ 11, 24, 26, 8]
Result of fourth query : 11 + 24 + 26 + 8 = 69
Result of first query : 1 + 2 + 3 + 4 = 10
A after second query: [ 11, 12, 13, 4]
A after third query : [ 11, 24, 26, 8]
Result of fourth query : 11 + 24 + 26 + 8 = 69
*************************************************************QUICK EXP****************************************
PROBLEM:
Given array A of N size and M operations of type:
- Add
v to all elements in a range. - Multiply
v to all elements in a range. - Reset all items to
v in a range. - Report sum in a range.
**********************************************************EDITORIAL*********************************************
IN THIS PROBLEM EACH NODE OF SEG LAZY TREE WILL CONTAINS 3 ELEMENTS
1- MUL MULTIPLICATION VALUE WHICH NEED TO MULTIPLY
2- ADDITION VAUE WHICH NEED TO ADD
3 - REPLACE VALUE WHICH NEED TO UPDATE
THERE ARE TWO PARTS TO UPDATE
(1) UPDATING THE LAZY NODE
FIRST WE CHECK THE LAZY NODE FOR A PENDING CHANGE OPERATION. IF THE NODE HAS A SET OPERATION THAT IS YET TO BE DONE THEN WE CHANGE THE SUM OF IN TREE AND COPY THE ADD VALUES AND MULITPLY VALUES TO THE LAZYS OF THE CHILD.
IF THERE IS ONLY ADD AND MUL OPERATION THAN WE DO THE OPERATION AS FOLLOWS
CHILD[NODE].SUM=LAZY[NODE].MUL*CHILD[NODE].SUM+LAZY[NODE].SUM
CHILD[NODE].MUL=LAZY[NODE].MUL*CHILD[NODE].MUL;
(2) THE SECONDPART IS UPDATING THE TREE NODE IF THE RANGE COPLETELY LIES IN THE UPDATED RABGE
(1) IF TYPE IS SET
THEN WE ADD THIS VALUE TO TREE [NODE] AND RESET THE CHILD LAZY(ADD,MULTIPLY) AND ONLY CARR THE SETVALUE TO THE LAZY.0
(2) IF THE PRESENT UPDATE IS ADD
THEN WE ADD TO THE ENTIRE RANGE IN TREE AND ADD THESE VALUES TO LAZY [CHILDNODE].ADD
(3) IF THE PRESENT UPDATE IS MULTIPLY THEN WE MULTIPLY THE LAZY NODES WITH THIS VALUE AND ALSO
MULTIPLY THE SUM WITH THIS VALUE.
SEE THE CODE FOR ILLUSTRATION
*********************************************************CODE*****************************
- #include<iostream>
- using namespace std;
- typedef long long int lli;
- #include<bits/stdc++.h>
- #define M 1000000007
- #include<vector>
- #include<stack>
- #include<queue>
- #include<set>
- #include<stdio.h>
- struct node
- {
- lli product;
- lli plush;
- lli reinit;
- } pending[1000000];
- lli seg[1000000];
- lli arr[1000000];
- lli ul,uu;
- lli qu,ql;
- int type;
- lli val;
- void update(lli index,lli begin,lli end)
- {
- if(pending[index].reinit!=0 || pending[index].plush!=0 || pending[index].product!=1)
- {
- int flag=0;
- if(pending[index].reinit!=0)
- {
- flag=1;
- seg[index]=((end-begin+1)*pending[index].reinit)%M;
- if(begin!=end)
- {
- pending[2*index].reinit=pending[index].reinit;
- pending[2*index+1].reinit=pending[index].reinit;
- pending[2*index].plush=pending[index].plush;//((pending[index].product*pending[2*index].plush)%M+pending[index].plush)%M;
- pending[2*index+1].plush=pending[index].plush;//((pending[index].product*pending[2*index+1].plush)%M+pending[index].plush)%M;
- pending[2*index].product=pending[index].product;//(pending[index].product * pending[2*index].product)%M;
- pending[2*index+1].product=pending[index].product;//(pending[2*index+1].product*pending[index].product)%M;
- }
- }
- seg[index]=((seg[index]*pending[index].product)%M+(pending[index].plush*(end-begin+1))%M)%M;
- if(begin!=end && flag==0)
- {
- // pending[2*index].reinit=pending[index].reinit;
- //pending[2*index+1].reinit=pending[index].reinit;
- pending[2*index].plush=((pending[index].product*pending[2*index].plush)%M+pending[index].plush)%M;
- pending[2*index+1].plush=((pending[index].product*pending[2*index+1].plush)%M+pending[index].plush)%M;
- pending[2*index].product=(pending[index].product*pending[2*index].product)%M;
- pending[2*index+1].product=(pending[2*index+1].product*pending[index].product)%M;
- }
- pending[index].product=1;
- pending[index].plush=0;
- pending[index].reinit=0;
- }
- if(begin>end || ul>end || uu<begin) return ;
- else if(begin>=ul && end<=uu)
- {
- if(type==1)
- {
- seg[index]=(seg[index]+(end-begin+1)*val)%M;
- if(end!=begin)
- {
- pending[2*index].plush=(pending[2*index].plush+val)%M;
- pending[2*index+1].plush= (pending[2*index+1].plush+val)%M;
- }
- }
- else if(type==2)
- {
- seg[index]=(seg[index]*val)%M;
- if(begin!=end)
- {
- pending[2*index].product=(pending[2*index].product*val)%M;
- pending[2*index+1].product=(pending[2*index+1].product*val)%M;
- pending[2*index].plush=(pending[2*index].plush*val)%M;
- pending[2*index+1].plush=(pending[2*index+1].plush*val)%M;
- }
- }
- if(type==3)
- {
- seg[index]=(val*(end-begin+1))%M;
- if(begin!=end)
- {
- pending[2*index].reinit=val;
- pending[2*index+1].reinit=val;
- pending[2*index].plush=0;
- pending[2*index+1].plush=0;
- pending[2*index].product=1;
- pending[2*index+1].product=1;
- }
- }
- return ;
- }
- update(2*index,begin,(begin+end)/2);
- update(2*index+1,(begin+end)/2+1,end);
- seg[index]= (seg[2*index]+seg[2*index+1])%M;
- }
- lli build(int index,int begin,int end)
- {
- if(begin==end)
- {
- seg[index]=arr[begin];
- return seg[index];
- }
- if(begin>end) return 0;
- build(2*index,begin,(begin+end)/2);
- build(2*index+1,(begin+end)/2+1,end);
- seg[index]=(seg[2*index+1]+seg[2*index])%M;
- }
- lli query(int index,int begin,int end)
- {
- if(pending[index].reinit!=0 || pending[index].plush!=0 || pending[index].product!=1)
- {
- int flag=0;
- if(pending[index].reinit!=0)
- {
- flag=1;
- seg[index]=((end-begin+1)*pending[index].reinit)%M;
- if(begin!=end)
- {
- pending[2*index].reinit=pending[index].reinit;
- pending[2*index+1].reinit=pending[index].reinit;
- pending[2*index].plush=pending[index].plush;//((pending[index].product*pending[2*index].plush)%M+pending[index].plush)%M;
- pending[2*index+1].plush=pending[index].plush;//((pending[index].product*pending[2*index+1].plush)%M+pending[index].plush)%M;
- pending[2*index].product=pending[index].product;//(pending[index].product * pending[2*index].product)%M;
- pending[2*index+1].product=pending[index].product;//(pending[2*index+1].product*pending[index].product)%M;
- }
- }
- seg[index]=((seg[index]*pending[index].product)%M+(pending[index].plush*(end-begin+1))%M)%M;
- if(begin!=end && flag==0)
- {
- // pending[2*index].reinit=pending[index].reinit;
- // pending[2*index+1].reinit=pending[index].reinit;
- pending[2*index].plush=((pending[index].product*pending[2*index].plush)%M+pending[index].plush)%M;
- pending[2*index+1].plush=((pending[index].product*pending[2*index+1].plush)%M+pending[index].plush)%M;
- pending[2*index].product=(pending[index].product*pending[2*index].product)%M;
- pending[2*index+1].product=(pending[2*index+1].product*pending[index].product)%M;
- }
- pending[index].product=1;
- pending[index].plush=0;
- pending[index].reinit=0;
- }
- if(begin>end || begin>qu || end<ql) return 0 ;
- if(begin>=ql && end<=qu)
- {
- return seg[index];
- }
- lli a=query(2*index,begin,(begin+end)/2);
- lli b=query(2*index+1,(begin+end)/2+1,end);
- return (a+b)%M;
- }
- int main()
- {
- int t;
- lli n,m;
- for(int i=0;i<n;i++)
- {
- }
- build(1,0,n-1);
- // cout<<" build seg array "<<endl;
- // for(int i=0;i<10;i++) cout<<seg[i]<<" ";
- //cout<<endl;
- for(int i=0;i<4*n+100;i++)
- {
- pending[i].product=1;
- pending[i].plush=0;
- pending[i].reinit=0;
- }
- for(int i=0;i<m;i++)
- {
- // lli type;
- //cout<<" type "<<type<<endl;
- if(type==1)
- {
- // cout<<"type1"<<endl;
- ul-=1;
- uu-=1;
- update(1,0,n-1);
- }
- if(type==2)
- {
- // cout<<"type2"<<endl;
- ul-=1;
- uu-=1;
- update(1,0,n-1);
- }
- if(type==3)
- {
- // cout<<"type3"<<endl;
- ul-=1;
- uu-=1;
- update(1,0,n-1);
- }
- if(type==4)
- {
- //cout<<"type4"<<endl;
- ql-=1;
- qu-=1;
- lli ans=query(1,0,n-1);
- cout<<ans<<endl;
- }
- }
- return 0;
- }
********************************************CHEF EDITORIAL******************************************************
QUICK EXPLANATION:
================
Build a segment tree, where at each node we storesum and variables mul and add , which denotes that the lazy update Ai←mul∗Ai+add needs to be applied. If required, we update the current node's sum and variables and propagate the laziness down the tree. Also, an multiplication update v at a node can be summarised as mul←mul∗v and add←add∗v and also addition update v at a node can be written as add←add+v . Set operation with value v can be written as mul←0 and add←v .
Build a segment tree, where at each node we store
EXPLANATION:
================
BASIC SEGMENT TREE STUFF
I assume you know how segment trees and lazy propagation work and the basic concepts behind them like time complexity, nodes, intervals. First, I'll introduce some terminology I'm going to use.
- Node: It's one of the nodes in the segment tree, it represents a contiguous interval of the array
A . - Interval of a node is the actual range covered by the node.
- Answer of a node is defined the actual value needed for interval queries. Here, for example, we need sum of values in the range.
Let's think step by step here.
- For range query problems, try to see if segment tree can be used.
- When does segment tree work? When you can merge two contiguous intervals(i.e nodes) to get answer of merged interval(node) in sublinear time. If complexity of merging is
O(K) , then complexity for each operation can be worst caseO(log N∗K) . For example, when we talk about range minimum function, we can get minimum value of merged interval by taking minimum of answers of two individual intervals. - Also, for range updates you need to use lazy propagation. What does lazy propagation require to work? It requires that for an interval if we have multiple update operations, we can calculate the answer for that interval without actually updating every element in that interval. For example, if we are trying to find range minimum and range updation query is increase all elements by value
v , then our new minimum is sum of existing minimum withv . Here also, we should be able to do this operation in sublinear time because it contributes to the factor of updations and queries.
Let's see how we use above two points to find here a solution using segment trees. Let's see the query part first: query is range sum, so merging two intervals is easy, just take the individual sums.
THE LAZY PROPAGATION SOLUTION
Now, let's see how we can handle all updations in such a way that we can find answer for an interval without actually updating the whole interval. We are going to store some data about the updations being done at that interval node and process it to find the answer. What could be this data? How do we find out? We need to observe what kind of operations we are doing. After some certain updations, our Ai could be transformed to something like ((Ai∗v1+v2)∗v3+v4+v5)∗v6+v7 , where v1 to v7 are values of range multiplication or range addition. Now, we can store all these values v1 to v7 at our node, but we might have to do O(number of queries) operations at each node, which is not really sublinear. We need to find a compact notation at each node interval.
Now, thing worth noting here is that Ai has been transformed to a linear function of Ai i.e. something of form (mul∗Ai+add) . Now, let's say I make one more multiplication range update v , what's the new value of Ai . It's (mul∗v∗Ai+add∗v) . So, we update mul ∗= v and add ∗= v at our node. Similarly, if we make a sum update with value v , the new value of Ai is (mul∗Ai+add+v) , so we update add += v . For setting all elements to v , we can just make one multiplication with 0 and then addition with value v .
So, if this interval is in range L to R , for interval sum, we need ∑Ri=L(mul∗Ai+add ) which we can write as (R−L+1)∗add+mul∗∑Ri=LAi . So, we have to store sum of original Ai and R and L (basically size) and two variables mul and add at each node. Now, to make things easier we can just directly store the sum of a node(i.e.* sum of all elements in that interval) instead of storing sum of original Ai . Then, for each range multiplication update or range addition update, we also update this sum along with the variables mul and add .
Also, as we do we in lazy propagation, we propagate the laziness to the children of a node, if we need to query a children of a lazy node. In this problem, we can individually propagate variables mul and add . So, if at a node mul≠1 , then we can say that this node is multiplication lazy and if required, we'll propagate this variable down to the children of this node. Similarly, if at a node add≠0 , we can say that this node is addition lazy and propagate this laziness down to its children.
COMPLEXITY:
For building the segment tree we need O(NlogN) and each query is O(logN) , so total complexity is O(NlogN+QlogN) .
No comments:
Post a Comment