这几天做过的几道线段树 HDU1698 HDU 2795 HDU 3308

本文解析了三道经典的线段树题目,包括HDU1698 Just a Hook、HDU2795 Billboard及HDU3308 LCIS。通过实战案例介绍了线段树的基本原理和应用技巧。


HDU 1698 Just a Hook 

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

这是我做的第一道线段树,由于是dota爱好者,看到屠夫就进来了。

题目大意:给你一个1到n连续的点,每个点刚开始权值为1,后来进行Q个操作

每个操作是对x到y之间的点(包括x和y)进行批量的权值变化,权值有1,2,3三种,

由于n有10W,Q也有10W,如果直接暴力对一个个点进行权值变化,最坏情况下10W*10W 必然超时。

这题可以用线段树。对区间进行操作,color为-1时表示这个区间内为多色,如果为1或者2或者3,插入和求和的时候就可以lazy了

对整个区间直接把color进行改变,插入时,递归回来的时候要更新父节点的数据。

时间复杂度为Q*logn

ContractedBlock.gif ExpandedBlockStart.gif View Code
  1 #include<iostream>
2
3 using namespace std;
4
5 int n;
6
7 const int MAXN = 100005;
8
9 inline int Left( int x ) {
10     return 2 * x;
11 }
12 inline int Right( int x ) {
13     return 2 * x + 1;
14 }
15
16 struct t {
17     int left;
18     int right;
19     int color;
20     
21     int mid () {
22         return ( right - left ) / 2 + left;
23     }
24     int length() {
25         return right - left + 1;
26     }
27 };
28
29 t tree[3*MAXN];
30
31 void build(int l, int r, int loc = 1) {
32     tree[loc].left = l;
33     tree[loc].right = r;
34     tree[loc].color = 1;
35
36     if ( l == r ) {
37         return ;
38     }
39
40     int mid = tree[loc].mid();
41     build( l, mid, Left(loc) );
42     build( mid + 1, r, Right(loc) );
43 }
44
45 void insert( int l, int r, int color, int loc = 1 ) {
46     if (tree[loc].left == l && tree[loc].right == r ) {
47         tree[loc].color = color;
48         return ;
49     }
50
51
52     if ( tree[loc].color != -1 ) {
53         tree[Left(loc)].color = tree[Right(loc)].color = tree[loc].color;
54     }
55
56     int mid = tree[loc].mid();
57     if ( r <= mid ) {
58         insert( l, r, color, Left(loc) );
59     } else if ( l > mid ) {
60         insert( l, r, color, Right(loc) );
61     } else {
62         insert( l, mid, color, Left(loc) );
63         insert( mid + 1, r, color, Right(loc) );
64     }
65
66     if ( tree[Left(loc)].color == tree[Right(loc)].color ) {
67         tree[loc].color = tree[Left(loc)].color;
68     } else {
69         tree[loc].color = -1;
70     }
71 }
72
73 int getSum( int l, int r, int loc = 1) {
74     if (tree[loc].left == l && tree[loc].right == r && tree[loc].color != -1) {
75         return tree[loc].color * tree[loc].length();
76     }
77
78     int mid = tree[loc].mid();
79     if ( r <= mid ) {
80         return getSum( l, r, Left(loc) );
81     } else if ( l > mid ) {
82         return getSum( l, r, Right(loc) );
83     } else {
84         return getSum( l, mid, Left(loc) ) + getSum( mid + 1, r, Right(loc) );
85     }
86 }
87
88 int main() {
89     int t;
90     scanf("%d", &t);
91     
92     for ( int T = 1; T <= t; T++ ) {
93         scanf("%d", &n);
94         build(1,n);
95
96         int Q;
97         scanf("%d", &Q);
98         while ( Q-- ) {
99             int left,right,color;
100             scanf("%d%d%d", &left, &right, &color);
101             insert(left,right,color);
102         }
103
104         printf("Case %d: The total value of the hook is %d.\n", T,getSum(1,n));
105     }
106
107     return 0;
108 }

  



HDU 2795 Billboard

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

题目大意:

这题是给你一个h*w的广告牌,然后给n个1*wi的广告,要求把广告贴上去,如果前面的行可以贴,就要贴前面的,前面的贴不下才可以贴下面,

广告的高度是wi,要求每个广告所在的行。

解题思路:

以行数为区间,建立线段树,他给的h有10^9次,是创不了10^9这么大的数组的。但是他给的n只有20W,也就是说最多只会用到前面20W行

所以线段树开个3*20W的数组就够了。

因为插入的时候是插入到一行的,所以不需用lazy思想,只需查找到底看下所剩的空间能不能让这个广告放下,如果不行则继续找,行的话就把

所剩空间减掉广告牌的宽度,然后输出这行就行了。

时间复杂度为nlogn

ContractedBlock.gif ExpandedBlockStart.gif View Code
 1 #include<iostream>
2
3 using namespace std;
4
5 const int MAXN = 200005;
6 int h,w,n;
7
8 inline int Left( int x ) {
9 return 2 * x;
10 }
11 inline int Right( int x ) {
12 return 2 * x + 1;
13 }
14 inline int Max( int a, int b ) {
15 return a > b? a : b;
16 }
17 inline int Min( int a, int b ) {
18 return a < b? a : b;
19 }
20
21 struct t {
22 int left;
23 int right;
24 int room;
25
26 int mid() {
27 return (right - left) / 2 + left;
28 }
29 };
30
31 t tree[MAXN*3];
32 bool flag;
33
34 void build( int l, int r, int loc = 1 ) {
35 tree[loc].left = l;
36 tree[loc].right = r;
37 tree[loc].room = w;
38
39 if ( l == r ) {
40 return;
41 }
42
43 int mid = tree[loc].mid();
44 build( l, mid, Left(loc) );
45 build( mid + 1, r, Right(loc) );
46 }
47
48 void insert( int wide, int loc = 1 ) {
49 if ( flag ) {
50 return ;
51 }
52
53 if ( wide > tree[loc].room ) {
54 return;
55 }
56
57 if ( tree[loc].left == tree[loc].right ) {
58 tree[loc].room -= wide;
59 printf("%d\n", tree[loc].mid());
60 flag = true;
61
62 return;
63 }
64
65 insert( wide, Left(loc) );
66 insert( wide, Right(loc) );
67
68 tree[loc].room = Max( tree[Left(loc)].room, tree[Right(loc)].room );
69 }
70
71
72
73 int main() {
74
75 while ( scanf("%d%d%d", &h, &w, &n) != EOF ) {
76 int length = Min(MAXN-5,h);
77
78 build(1,length);
79 while ( n-- ) {
80 int x;
81 scanf("%d", &x);
82 flag = false;
83 if ( x > tree[1].room ) {
84 printf("-1\n");
85 } else {
86 insert(x);
87 }
88 }
89 }
90 return 0;
91 }

  

HDU 3308 LCIS

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

题目大意:

这题题目很简单,求某个段的最长连续递增子序列 ,实时更新某个点的值和查询某段的最长连续递增子序列。

解题思路:

这题线段树的结构体里要多放些信息:

这个段最左边的值lval,最右边的值rval,

从这个段最左边的点开始的最长连续递增子序列lmax,

从这个段最右边的点结束的最长连续递增子序列rmax,

还有这个段的最长连续递增子序列max

这些都是为了更新父节点,max则是这个段的答案。

更新父节点的时候,根据左子结点的rval和右子结点的lval的比较来更新父节点的信息。

更新函数update是结构体的成员函数。

因为插入是更新某个点的值,所以插入的时候搜到底就行了。

查询的时候两边要合在一起的时候一直错,纠结了好几天,找出哪里错误后发现改不了。

后来看了别人的解题报告,如果结果需要两边合在一起的话,我把查找的返回值就设为线段树的那个结构体类型,

然后把这2个返回值当做某个节点的左子节点和右子节点,然后调用这个节点的成员函数update,这个临时的节点的max成员就是我所要的答案了

#include<iostream>
#include<string>
using namespace std;

const int MAXN = 100005;

inline int Left( int x ) {
    return 2 * x;
}
inline int Right( int x ) {
    return 2 * x + 1;
}
inline int Max( int a, int b ) {
    return a > b? a : b;
}
inline int Min( int a, int b ) {
    return a < b? a : b;
}

struct t {
    int max;
    int lmax;
    int rmax;
    int lval;
    int rval;
    int left;    
    int right;

    int mid() {
        return (right - left) / 2 + left;
    }

    int length() {
        return right - left + 1;        
    }

    void update(t a, t b) {
        left = a.left;
        right = b.right;
        lval = a.lval;
        rval = b.rval;


        if ( a.lmax == a.length()  && b.lval > a.rval ) {
            lmax = a.lmax + b.lmax;
        } else {
            lmax = a.lmax;
        }

        if ( b.rmax == b.length() && b.lval > a.rval) {
            rmax =  a.rmax + b.rmax;
        } else {
            rmax = b.rmax;
        }

        if ( b.lval > a.rval ) {
            max = Max( Max(a.max, b.max),a.rmax + b.lmax );
        } else {
            max = Max(a.max, b.max);
        }

    }
};

t tree[MAXN*3];
int num[MAXN];
bool flag = false;
bool isRight = false;


void build(int l, int r, int loc = 1) {
    tree[loc].left = l;
    tree[loc].right = r;
    tree[loc].lval = num[l];
    tree[loc].rval = num[r];

    if ( l == r ) {
        tree[loc].max = 1;
        tree[loc].lmax = 1;
        tree[loc].rmax = 1;

        return;
    }    

    int mid = tree[loc].mid();
    build(l, mid, Left(loc) );
    build(mid+1, r, Right(loc) );

    tree[loc].update(tree[Left(loc)], tree[Right(loc)] );
}

void insert(int index, int value, int loc = 1 ) {

    if ( index < tree[loc].left || index > tree[loc].right ) {
        return;
    }
    if ( flag ) {
        return;
    }

    if  ( tree[loc].left == tree[loc].right ) {
        if ( tree[loc].left ==  index ) {
            tree[loc].lval = tree[loc].rval = value;
            flag = true;
        }
        return;
    }

    insert( index,value, Left(loc) );    
    insert( index,value, Right(loc) );

    tree[loc].update( tree[Left(loc)], tree[Right(loc)] );

}

t Query(  int l, int r, int loc = 1 ) {
    if ( l > tree[loc].right || r < tree[loc].left ) {
        t temp;
        temp.max = -1;
        return temp;
    }

    if ( l <= tree[loc].left && tree[loc].right <= r ) {
        return tree[loc];
    }

    t a,b;
    int mid = tree[loc].mid();   
    a = Query(l, r, Left(loc) );
    b = Query(l, r, Right(loc) );

    if ( -1 == a.max ) {
        return b;
    } else if ( -1 == b.max ){
        return a;
    } else {
        t temp;
        temp.update(a,b);
        return temp;
    }

}



int main() {
    int T;
    scanf("%d", &T);
    while (T-- ) {
        memset(tree, 0, sizeof(tree) );
        int n,m;
        scanf("%d%d", &n, &m);

        for ( int i = 1; i <= n; i++ ) {
            scanf("%d", num + i);
        }
        build(1,n);
        while (m--) {
            char ope[123];
            int a,b;
            scanf("%s%d%d", ope, &a, &b); //下标0开始
            if ( strcmp(ope,"U") == 0 ) {
                flag = false;
                a++;
                insert(a,b);
            } else {
                a++;
                b++;           
                printf("%d\n",Query(a,b).max);
            }
        }

    }
    return 0;
}

  


转载于:https://www.cnblogs.com/mkgg2010/archive/2011/08/22/2150039.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值