P1198 [JSOI2008] Maximum number
-
- Topic provider The user does not exist
- Label segment Tree Each province is selected
- Difficulty Increase +/province selection-
Submit a discussion of the problem record
Recent discussions
- WA80 's poking this qwq
- Bzoj all over, Rokua unexpectedly can't live ...
- Why can't you live?
- = = I would like to say that this question is added to the reading of WA? ...
- Who said Pascal only 80, to change C + + ...
- What is 80 for a segment tree?
Title Description
Now you are asked to maintain a sequence that requires the following two actions:
1, query operation.
Syntax: Q L
Function: Query the maximum number of L in the number of the end of the current sequence and output the value of this number.
Limit: l does not exceed the length of the current series.
2, insert operation.
Syntax: A n
function: N plus t, where T is the answer to the most recent query operation (t=0 if the query operation has not been performed), and the resulting result is modeled on a fixed constant d and the resulting answer is inserted at the end of the sequence.
Limit: N is an integer (possibly negative) and within a long range.
Note: The initial sequence is empty and does not have a number.
Input output Format input format:
The first line is two integers, M and D, where m represents the number of operations (M <= 200,000), and D is satisfied (0<d<2,000,000,000) as described above
The next M-line, one string per line, describes a specific operation. The syntax is as described above.
Output format:
For each query operation, you should output the results sequentially, with each result in one row.
Input and Output Sample input example # #:
5 100A 96Q 1 a 97Q 1Q 2
Sample # # of output:
969396
Description
[JSOI2008]
Analysis: This problem has a lot of ways to solve, the first can be found in the number of series is incremented, each add to the number is larger than before, then according to this principle, simulation can be made.
This question can be used to practice line segment tree, why think to use line tree? Notice the interval two words! Find the maximum value in the interval and complete the single-point modification (insert), isn't that the most basic operation of the segment tree? Because the segment tree can be all assigned to-inf, the insert operation can be understood as a single point of modification, and the template of the line segment tree is solved.
The variable is turned into a global variable and checked for a night's error ...
#include <cstdio>#include<cstring>#include<iostream>#include<algorithm>#defineLe L,mid,o * 2#defineRe mid + 1,r,o * 2 + 1using namespacestd;Const intMAXN =200001;intM, D,maxo[maxn <<2],len,t;voidBuildintLintRinto) { if(L = =R) {Maxo[o]= -2147283647; return; } intMid = (L + r) >>1; Build (LE); Build (re);}voidCharu (intLintRintOintIintj) { if(L = =R) {Maxo[o]=J; return; } intMid = (L + r) >>1; if(I <=mid) Charu (Le, I, j); ElseCharu (Re, I, j); Maxo[o]= Max (Maxo[o *2], Maxo[o *2+1]);}intQueryintLintRintOintXinty) { if(x <= l && R <=y)returnMaxo[o]; intMid = (L + r) >>1; inttemp =-2147483647; if(x <=mid) Temp=Max (Temp,query (Le, x, y)); if(Y >mid) Temp=Max (temp, query (re, x, y)); returntemp;}intMain () {scanf ("%d%d", &m, &d); Build (1, MAXN,1); for(intb =1; b <= m;++b) {CharCinti; CIN>> c;scanf ("%d", &i); if(c = ='A') {Len++;charu (1, MAXN,1, Len, (i + t)%d); } Else{t = query (1, MAXN,1, Len-i +1, Len);p rintf ("%d\n", T); } } return 0;}
If you do not have a line tree, then you can refer to the following paragraph of the text (previously written may not be very good, look for understanding, there may be some incorrect, can only be used as a reference):
Line tree, this magnum tree.
Line segments, line segments, plainly is an interval, the main operation of the segment tree is to modify the interval to query, the efficiency is very high, the use of line tree is very wide, single-point update, Interval update, the maximum value of the inquiry, Interval inquiry, as to what it can do depends on the amount of information stored in the tree.
This is a graph of line segment tree, this figure just can help us understand the general shape of the segment tree, and can not tell us more information, in fact, more functions of the line tree is hidden behind each node of the information, in order to be more convenient to do the problem, we give each node of the segment tree numbered. We label from top to bottom, from left to right, if the root node ordinal is k, then its left subtree node ordinal is 2k, right subtree node ordinal is 2k + 1, each sequence number corresponds to the only node, so we can use an array tree to represent the hidden information behind the node. How much does this array actually open? Although we do not need to consider this carefully in the normal work, but in some of the memory constraints are very tight topics are to pay attention to. If the interval range is [0,n-1], then the size of the tree is M=2*n + 1, which is well verified.
We first consider how to make a contribution, generally speaking, as long as the leaf node directly input is good, but how can we quickly reach the leaf node? Recursion!
int tree[2 * max_n + 1];
/* Create a segment tree with k as the root node [l,h] for the operating interval */
void Built_tree (int k, int L, int H)
{
if (L = = H) {
scanf ("%d", &tree[k]);
Return
}
Built_tree (k << 1, L, (L + H) << 1);
Built_tree (k << 1 | 1, (L + H) << 1 | 1, h);
}
If l==h, the length of the current interval is 1, that is, the node is a leaf node, can be directly assigned to the value.
Consider a classic question: Find the minimum element value within a range.
This problem can be done with violence, but the complexity is too high, in some topics may be tle, we can see the interval two words, then this problem 80% to be done with a line segment tree (of course, is not absolute, but high efficiency), we constantly compare the current query interval and target range, if the current sany between the target range, Then the nodes represented by the current depth can participate in the minimum calculation, if not within the interval, then return infinity, otherwise the current tree of the left and right subtree of the same operation (perhaps the term is too strong).
int read_tree (int k, int L, int H, int beg, int end)
{
if (Beg > H | | End < L) Return-int_max;
if (Beg <= L && end >= H) return tree[k];
return min (Read_tree (2 * k, L, (L + H)/2, Beg, end),
Read_tree (2 * k + 1, (L + H)/2 + 1, h, beg, end));
}
There is a query, it must accompany the existence of the modification, if it is an ordinary array, the modification is very easy, only need to manipulate the subscript of the corresponding data modification can, but this is a high-efficiency data structure, modification means to change a lot of quantities, in the line tree, We modify a node only by modifying it and all its ancestors, and the rest is unchanged.
/* Update the value at ID to key*/in the segment tree where the root node is k,[l,h] as the operating interval
int update_tree (int k, int L, int H, int id, int key)
{
if (L = = H) {
TREE[K] = key;
Return
}
if (ID < (L + H)/2)
Update (k * 2, L, (L + H)/2, ID, key);
Else
Update (K * 2 + 1, (L + H)/2 + 1, h, ID, key);
Tree[k] = MAX (TREE[K * 2], tree[2 * k + 1]);
}
This completes the modification operation.
Then a more complex interval modification, design a data structure, so that it supports two kinds of operations
- Add (l,r,v) will al,al+1 ... The value of Ar all +v
- Query (l,r) computes the subsequence al,al+1 ... AR element and, Min., maximum value.
Here to maintain three query values, how to maintain it?
First of all, the add operation here is the interval modification, not a single point of modification, the worst case may be the entire tree line node value will be modified. We know that any interval of the segment tree can be decomposed into no more than 2h disjoint intervals, and using this conclusion we can decompose each add operation into an add operation of no more than 2h, recorded in the node of the segment tree. Each time an add operation is performed, the additional information for each node is recalculated, and the nodes that are recursively accessed are recalculated, and are evaluated after recursion!
Here's the code for the calculation:
void Weihu (int o,int l,int R)
{
int LC = O * 2, rc = O * 2 + 1;
Sumv[o] = Minv[o] = Maxv[o] = 0;
if (R > L) {
Sumv[o] = SUMV[LC] + SUMV[RC];
Minv[o] = min (MINV[LC], MINV[RC]);
Maxv[o] = max (MAXV[LC], MAXV[RC]);
}
Minv[o] + = Addv[o];
Maxv[o] + = Addv[o];
Sumv[o] + = addv[o] * (r-l + 1);
}
For the following code, the scope of the modification/query is [y1,y2].
Here's the SUMV array to say, why use the left and right sub-nodes to add it? First, the Father node contains the left and right nodes, followed by the maintenance of the time will not need to modify all the elements of the SUMV array. Of course, this refers to a special case, which is generally the kind of extreme data.
The following is the code for the add operation:
void Add (int o, int L, int R)
{
int LC = O * 2, rc = O * 2 + 1;
if (y1 <= L && y2 >= R)
Addv[o] + = V;
else {
int M = (L + R) >> 1;
if (y1 <= M)
ADD (LC, L, M);
if (y2 > M)
ADD (RC, M + 1, R);
}
Weihu (o, L, R);
}
Where the ADDV array is the add value of the additive boundary, because a sub-node of a segment tree may not be known to be modified once, it is necessary to set up this array.
Then is the query operation, say with line segment tree step is careful, feel this sentence is correct ah, each operation should consider to the node to the node has no effect, our query is generally from the top down recursive query, since the parent node of a node has performed an add operation, and this node is also included by the parent, So the value of this node must be changed, so we can only set 3 global variables to maintain.
int _min, _max, _sum;
void query (int o, int L, int R, int add)
{
if (y1 <= L && y2 >= R) {
_sum + = Sumv[o] + Add * (R-l + 1); \
_min = min (_min, Minv[o] + add);
_max = Max (_max, Maxv[o] + add);
}
else {
int M = (L + R) >> 1;
if (y1 <= M)
Query (o * 2, L, M, add + addv[o]);
if (y2 > M)
Query (o * 2 + 1, M + 1, R, add + addv[o]);
}
}
See a lot of people are confused, OK, actually I also a bit dizzy. Someone might ask why our Weihu function has been maintained and now we have to maintain it? Because the Weihu function is from the bottom to the top, that is, from the left and right child nodes maintained, is relative to the child node changes, and here the global variable is because the parent node has an add operation, child nodes are included, so to open variable maintenance. If you still do not understand, you can see that the Weihu function finally only modifies the value of the current node, and does not maintain to its child nodes, so to open variable maintenance.
The next step is more complex:
Set (L,R,V) modifies the value of al,al+1...ar to V.
Query (L,r) calculates the three values of the subsequence al,al+1...ar (same as the previous question).
You can see that the set operation is changed here, and we say that the problem is more complicated than before, why? Because the previous add operation, regardless of the sequence of operations, can achieve the final result, if the algorithm is correct, the code is not written wrong. However, set operation is different, like brush paint, the final brush is the final color. What do we do? Hit Mark! The marking here is equivalent to the change of the special situation to do the change, is to find out the last three values to hit, then how to do? If the current interval is completely contained within the interval that we need to modify/query, then the direct modification is marked V, otherwise the tag is passed.
void pushdown (int o)
{
int LC = O * 2, rc = O * 2 + 1;
if (Setv[o] >= 0)
{
SETV[LC] = SETV[RC] = Setv[o];
Setv[o] =-1;
}
}
Here the Setv array is the tag, notice that the array is initialized to-1, here do not make a mistake, then the question is: why do we have to clear the parent node tag?
Next, set operation code:
void Set (int o, int L, int R)
{
int LC = O * 2, rc = O * 2 + 1;
if (y1 <= L && y2 >= R)
{
Setv[o] = v;
}
else {
Pushdown (o);
int M = (L + R) >> 1;
if (y1 <= M)
Set (LC, L, M);
Else
Maintain (LC, L, M);
if (y2 > M)
Set (RC, M + 1, R);
Else
Maintain (RC, M + 1, R);
}
Maintain (o, L, R);
}
Notice that 3 times maintain, the last time well understood, because we have said before, every time after the completion of the delivery must be maintained once, then the first two why? Because once the tag is passed, the additional information of the subtree needs to be changed, the subtree in the current interval will be maintained after recursion, but the subtree within the other interval is not maintained, so it is necessary to add two calls to the maintain function.
Next is the code for the query operation:
void query (int o, int L, int R)
{
if (Setv[o] >= 0) {
_sum + = setv[o] * (min (R, y2)-Max (L, y1) + 1);
_min = min (_min, setv[o]);
_max = Max (_max, Setv[o]);
}
else if (y1 <= L && y2 >= R)
{
_sum + = Sumv[o];
_min = min (_min, minv[o]);
_max = Max (_max, Maxv[o]);
}
else {
int M = (L + R) >> 1;
if (y1 <= M)
Query (o * 2, L, M);
if (y2 > M)
Query (o * 2 + 1, M + 1, R);
}
}
For the marked interval, we have to prioritize, first know that the current interval has been modified to setv[o], since all the values are the same, it is natural to manipulate them, and then consider the interval to be queried to completely surround. Back to the previous question, why should we clear the tag of the parent node? We will mark the next pass is generally transmitted to the interval to be queried by the interval completely surrounded, because the value of the sub-node within the interval contains the value of a large interval, in other words, the result is a number of inter-cell and, and these are decomposed to decomposition to no longer, natural, We will eliminate the parent node's tag because the child node is the root of the effect, and the value we are seeking is finally done in the child node, so we want to eliminate it.
Rokua P1198 [JSOI2008] Maximum number