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


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


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;
}