hdu4831 Scenic Popularity(线段树)
题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=4831
题目大概意思就是有多个风景区和休息区,每个风景区有热度,休息区的热度与最接近的分景区的热度相同,题目要求求出小于等于给定热度值的风景区和休息区的个数。显然如果直接暴力的话,复杂度为O(TKN),达到10^9次方数量级,复杂度过高,对于这种问答的题目,最一般的思路其实是线段树,线段树更改和查询的时间复杂度均为O(logn),所以如果用线段树的话,这道题目的复杂度为O(TKlogH),达到10^5左右的数量级,这个复杂度可以满足题目要求。
线段树的实现一般用完全二叉树来实现,每个节点都可以看做是一段"线段"或者某个”范围“(x,y],这道题目节点表示的”范围“就是某段景区热度值(x,y],同时节点还记录了在该”范围“中包含的景区和休息区的个数,所以树的叶子节点就是等于某个热度值的景区和休息区的个数。线段树的定义如下:
#define MAX_SIZE 100000 typedef struct _tree { int count; int left, right; }TreeNode; TreeNode tree[9 * MAX_SIZE + 5]; void Init(int x, int left, int right) { tree[x].count = 0; tree[x].left = left; tree[x].right = right; if (left + 1 == right) return; Init(x << 1, left, (left + right) >> 1); Init((x << 1) + 1, (left + right) >> 1, right); } void Update(int x, int coord, int count) { int left = tree[x].left; int right = tree[x].right; int mid = (left + right) >> 1; if (left + 1 == right) { tree[x].count += count; return; } if (coord <= mid) Update(x << 1, coord, count); else Update((x << 1) + 1, coord, count); tree[x].count += count; } int Query(int x, int coord) { int left = tree[x].left; int right = tree[x].right; int mid = (left + right) >> 1; if (left + 1 == right) return tree[x].count; if (coord <= mid) return Query(x << 1, coord); else if (coord > mid) return tree[x << 1].count + Query((x << 1) + 1, coord); }
每次更新某个景区的热度值时,受影响的是该景区和离该景区近的休息区的热度值,其它的景区和热度值是保持不变的,只要预处理离每个景区近的休息区的个数,就可以在更新某个景区的热度值后立马得到哪些景区和休息区的热度值受到影响,从而更新线段树。不过需要注意的是某个休息区同时与两个景区距离相同的情况。我的方法是对每个景区和休息区设置两个变量li和ri,如果是景区,li表示向前所能影响的最前的休息区,ri表示向后所能影响的最后的休息区;如果是休息区,并且有两个风景区到该休息区的距离相等,li和ri分别表示这两个风景区。有个这两个变量,对于给定的风景区,就能直接计算该风景区所影响的休息区个数。
typedef struct _xh { int x; int h; int li, ri; }XH; XH data[10005]; int GetCount(int x) { int cnt = data[x].ri - data[x].li + 1; if (data[x].li < x && data[data[x].li].li != -1 && data[data[data[x].li].li].h >= data[x].h) cnt--; if (data[x].ri > x && data[data[x].ri].ri != -1 && data[data[data[x].ri].ri].h > data[x].h) cnt--; return cnt; }
有了上面的分析,对于给定的热度值获取小于等于这个热度值的景区和休息区的个数就简单了,直接查询线段树就可以了。
具体代码如下:
1 #include <stdio.h> 2 #include <string.h> 3 4 #define MAX_SIZE 100000 5 typedef struct _tree 6 { 7 int count; 8 int left, right; 9 }TreeNode; 10 TreeNode tree[9 * MAX_SIZE + 5]; 11 typedef struct _xh 12 { 13 int x; 14 int h; 15 int li, ri; 16 }XH; 17 XH data[10005]; 18 19 typedef struct _qy 20 { 21 char op; 22 int x, h; 23 }QY; 24 QY qy[105]; 25 26 void Init(int x, int left, int right) 27 { 28 tree[x].count = 0; 29 tree[x].left = left; 30 tree[x].right = right; 31 if (left + 1 == right) 32 return; 33 Init(x << 1, left, (left + right) >> 1); 34 Init((x << 1) + 1, (left + right) >> 1, right); 35 } 36 void Update(int x, int coord, int count) 37 { 38 int left = tree[x].left; 39 int right = tree[x].right; 40 int mid = (left + right) >> 1; 41 if (left + 1 == right) 42 { 43 tree[x].count += count; 44 return; 45 } 46 if (coord <= mid) 47 Update(x << 1, coord, count); 48 else 49 Update((x << 1) + 1, coord, count); 50 tree[x].count += count; 51 } 52 int Query(int x, int coord) 53 { 54 int left = tree[x].left; 55 int right = tree[x].right; 56 int mid = (left + right) >> 1; 57 if (left + 1 == right) 58 return tree[x].count; 59 if (coord <= mid) 60 return Query(x << 1, coord); 61 else if (coord > mid) 62 return tree[x << 1].count + Query((x << 1) + 1, coord); 63 } 64 int GetCount(int x) 65 { 66 int cnt = data[x].ri - data[x].li + 1; 67 if (data[x].li < x && data[data[x].li].li != -1 && data[data[data[x].li].li].h >= data[x].h) 68 cnt--; 69 if (data[x].ri > x && data[data[x].ri].ri != -1 && data[data[data[x].ri].ri].h > data[x].h) 70 cnt--; 71 return cnt; 72 } 73 int main(void) 74 { 75 int t, n, k, i, j, pl, pr, cnt, max, x, h, cntpre, cntnext, cntnew, cntnpre, cntnnext; 76 char op; 77 78 scanf("%d", &t); 79 for (i = 0; i < t; i++) 80 { 81 max = 0; 82 memset(data, -1, sizeof(data)); 83 scanf("%d", &n); 84 for (j = 0; j < n; j++) 85 { 86 scanf("%d%d", &data[j].x, &data[j].h); 87 if (max < data[j].h) 88 max = data[j].h; 89 } 90 scanf("%d", &k); 91 for (j = 0; j < k; j++) 92 { 93 scanf("%1s", &qy[j].op); 94 if (qy[j].op == 'Q') 95 scanf("%d", &qy[j].h); 96 else 97 scanf("%d%d", &qy[j].x, &qy[j].h); 98 if (max < qy[j].h) 99 max = qy[j].h; 100 } 101 pl = pr = 0; 102 while (pl < n && !data[pl].h) 103 pl++; 104 data[pl].li = 0; 105 pr = pl + 1; 106 while (pl < n) 107 { 108 while (pr < n && !data[pr].h) 109 pr++; 110 if (pr == n) 111 break; 112 if (pl + 1 == pr) 113 { 114 data[pl].ri = pl; 115 data[pr].li = pr; 116 } 117 else 118 { 119 j = pl + 1; 120 while (j < pr) 121 if (data[j]. x - data[pl].x < data[pr].x - data[j].x) 122 { 123 data[pl].ri = j; 124 data[pr].li = j + 1; 125 j++; 126 } 127 else if (data[j]. x - data[pl].x == data[pr].x - data[j].x) 128 { 129 data[pl].ri = data[pr].li = j; 130 data[j].li = pl; 131 data[j].ri = pr; 132 break; 133 } 134 else 135 { 136 data[pr].li = j; 137 data[pl].ri = j - 1; 138 break; 139 } 140 } 141 pl = pr++; 142 } 143 if (pl < n) 144 data[pl].ri = n - 1; 145 Init(1, 0, max); 146 for (j = 0; j < n; j++) 147 if (data[j].h) 148 { 149 cnt = GetCount(j); 150 Update(1, data[j].h, cnt); 151 } 152 printf("Case #%d:\n", i + 1); 153 for (j = 0; j < k; j++) 154 if (qy[j].op == 'Q') 155 printf("%d\n", Query(1, qy[j].h)); 156 else 157 { 158 x = qy[j].x; 159 h = qy[j].h; 160 cnt = GetCount(x); 161 Update(1, data[x].h, -cnt); 162 cntpre = cntnext = cntnpre = cntnnext = 0; 163 if (data[x].li < x && data[data[x].li].li != -1) 164 cntpre = GetCount(data[data[x].li].li); 165 if (data[x].ri > x && data[data[x].ri].ri != -1) 166 cntnext = GetCount(data[data[x].ri].ri); 167 data[x].h = h; 168 cntnew = GetCount(x); 169 Update(1, data[x].h, cntnew); 170 if (data[x].li < x && data[data[x].li].li != -1) 171 cntnpre = GetCount(data[data[x].li].li); 172 if (data[x].ri > x && data[data[x].ri].ri != -1) 173 cntnnext = GetCount(data[data[x].ri].ri); 174 if (cntpre != cntnpre) 175 Update(1, data[data[data[x].li].li].h, cntnpre - cntpre); 176 if (cntnext != cntnnext) 177 Update(1, data[data[data[x].ri].ri].h, cntnnext - cntnext); 178 } 179 } 180 return 0; 181 }