线段树(区间更新)小结

来源:互联网 发布:公务员网络大讲堂 编辑:程序博客网 时间:2024/06/01 07:29

线段树是ACM竞赛的常客,由于它的高效,美妙,经常被拿来出题。线段树是解决与区间有关的问题的,其中有一类问题就是更新区间上的一段值,这是非常典型的一类问题,在此,我做个小结。

倘若要更新一个区间的值,我们每次更新到叶子节点,那么这样效率就非常低了。倘若我们能每次只更新到你需要的节点,然后在该节点做个标记(就是lazy标记),下次要用到该节点下的东西,再更新下面的值。举一个经典例子,HDU1698。

Just a Hook

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 29463    Accepted Submission(s): 14555


Problem Description
In the game of DotA, Pudge’s meat hook is actually the most horrible thing for most of the heroes. The hook is made up of several consecutive metallic sticks which are of the same length.



Now Pudge wants to do some operations on the hook.

Let us number the consecutive metallic sticks of the hook from 1 to N. For each operation, Pudge can change the consecutive metallic sticks, numbered from X to Y, into cupreous sticks, silver sticks or golden sticks.
The total value of the hook is calculated as the sum of values of N metallic sticks. More precisely, the value for each kind of stick is calculated as follows:

For each cupreous stick, the value is 1.
For each silver stick, the value is 2.
For each golden stick, the value is 3.

Pudge wants to know the total value of the hook after performing the operations.
You may consider the original hook is made up of cupreous sticks.
 

Input
The input consists of several test cases. The first line of the input is the number of the cases. There are no more than 10 cases.
For each case, the first line contains an integer N, 1<=N<=100,000, which is the number of the sticks of Pudge’s meat hook and the second line contains an integer Q, 0<=Q<=100,000, which is the number of the operations.
Next Q lines, each line contains three integers X, Y, 1<=X<=Y<=N, Z, 1<=Z<=3, which defines an operation: change the sticks numbered from X to Y into the metal kind Z, where Z=1 represents the cupreous kind, Z=2 represents the silver kind and Z=3 represents the golden kind.
 

Output
For each case, print a number in a line representing the total value of the hook after the operations. Use the format in the example.
 

Sample Input
11021 5 25 9 3
 

Sample Output
Case 1: The total value of the hook is 24.

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#include <cmath>#include <queue>#include <stack>#include <vector>#include <map>#include <set>#include <deque>#include <cctype>#define LL long long#define INF 0x7fffffffusing namespace std;const int maxn = 100005;int sum[maxn << 2];//求区间和 int lazy[maxn << 2];//延迟标记 void pushdown(int rt, int m) {if(lazy[rt]) {//如果之前这里做了标记,则说明没有往下更新,暂停了一下,用来判断是否需要往下更新 lazy[rt << 1] = lazy[rt << 1 | 1] = lazy[rt];sum[rt << 1] = (m - (m >> 1)) * lazy[rt];sum[rt << 1 | 1] = (m >> 1) * lazy[rt];lazy[rt] = 0;//往下更新完后,标记置为0,即当前不需要往下更新 }}void build(int l, int r, int rt) {lazy[rt] = 0;sum[rt] = r - l + 1;if(l == r) return;int mid = (l + r) >> 1;build(l, mid, rt << 1);build(mid + 1, r, rt << 1 | 1);}void update(int L, int R, int c, int l, int r, int rt) {if(L <= l && r <= R) {sum[rt] = c * (r - l + 1);lazy[rt] = c;//延迟标记,每次把该段更新完后暂时不往下更新,节省时间,这里特别注意和累加的区别,累加是为整个区间增加多少值 return;}pushdown(rt, r - l + 1);//向下更新 int mid = (l + r) >> 1;if(L <= mid) update(L, R, c, l, mid, rt << 1);if(R >= mid + 1) update(L, R, c, mid + 1, r, rt << 1 | 1);sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];//向上更新 }int main() {int T, n, m;scanf("%d", &T);for(int cas = 1; cas <= T; cas ++) {scanf("%d %d", &n, &m);build(1, n, 1);for(int i = 0; i < m; i ++) {int a, b, c;scanf("%d %d %d", &a, &b, &c);update(a, b, c, 1, n, 1);}printf("Case %d: The total value of the hook is %d.\n", cas, sum[1]);}return 0;}

当然,有些时候选择更新到那个节点,做了标记之后要怎么再往下更新,还是得靠点脑洞的。

Can you answer these queries?

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65768/65768 K (Java/Others)
Total Submission(s): 14238    Accepted Submission(s): 3317


Problem Description
A lot of battleships of evil are arranged in a line before the battle. Our commander decides to use our secret weapon to eliminate the battleships. Each of the battleships can be marked a value of endurance. For every attack of our secret weapon, it could decrease the endurance of a consecutive part of battleships by make their endurance to the square root of it original value of endurance. During the series of attack of our secret weapon, the commander wants to evaluate the effect of the weapon, so he asks you for help.
You are asked to answer the queries that the sum of the endurance of a consecutive part of the battleship line.

Notice that the square root operation should be rounded down to integer.
 

Input
The input contains several test cases, terminated by EOF.
  For each test case, the first line contains a single integer N, denoting there are N battleships of evil in a line. (1 <= N <= 100000)
  The second line contains N integers Ei, indicating the endurance value of each battleship from the beginning of the line to the end. You can assume that the sum of all endurance value is less than 263.
  The next line contains an integer M, denoting the number of actions and queries. (1 <= M <= 100000)
  For the following M lines, each line contains three integers T, X and Y. The T=0 denoting the action of the secret weapon, which will decrease the endurance value of the battleships between the X-th and Y-th battleship, inclusive. The T=1 denoting the query of the commander which ask for the sum of the endurance value of the battleship between X-th and Y-th, inclusive.
 

Output
For each test case, print the case number at the first line. Then print one line for each query. And remember follow a blank line after each test case.
 

Sample Input
101 2 3 4 5 6 7 8 9 1050 1 101 1 101 1 50 5 81 4 8
 

Sample Output
Case #1:1976

#include <cstdio>#include <cstring>#include <iostream>#include <cmath>#include <algorithm> #define lson l, m, rt<<1#define rson m+1, r, rt<<1|1using namespace std;typedef long long LL;const int maxn = 100010;LL sum[maxn*4];int n, m;void PushUp(int rt){    sum[rt] = sum[rt<<1] + sum[rt<<1|1];    return ;}void build(int l, int r, int rt){    sum[rt] = 0;    if(l == r)    {        scanf("%I64d", &sum[rt]);        return ;    }    int m = (l+r)>>1;    build(lson);    build(rson);    PushUp(rt);}void update(int L, int R, int l, int r, int rt){    if(l == r)    {        sum[rt] = sqrt(1.0 * sum[rt]);        return ;    }    if(L<=l && r<=R && sum[rt]==r-l+1)  //此时每个数都为1  没必要更新     {        return ;    }    int m = (l+r)>>1;    if(L <= m) update(L, R, lson);    if(m < R) update(L, R, rson);    PushUp(rt);}LL query(int L, int R, int l, int r, int rt){    if(L<=l && r<=R)    {        return sum[rt];    }    int m = (l+r)>>1;    LL ans = 0;    if(L <= m)   ans += query(L, R, lson);    if(m < R)    ans += query(L, R, rson);    return ans;}int main(){    int test = 0;    while(scanf("%d", &n) != EOF)    {        build(1, n, 1);        int a, b, c;        int x, y;        scanf("%d", &m);        printf("Case #%d:\n", ++test);        while(m--)        {            scanf("%d%d%d", &a, &b, &c);            x = min(b, c);            y = max(b, c);            if(a == 0)            {                update(x, y, 1, n, 1);            }            else            {                printf("%I64d\n", query(x, y, 1, n, 1));            }        }        puts("");    }    return 0;}


1 0