POJ 2528 (线段树 离散化) Mayor's posters

离散化其实就是把所有端点放在一起,然后排序去个重就好了。

比如说去重以后的端点个数为m,那这m个点就构成m-1个小区间。然后给这m-1个小区间编号1~m-1,再用线段树来做就行了。

具体思路是,从最后一张的海报来判断,如果海报覆盖的区域有空白区域那么这张海报就是可见的。并及时更新线段树信息。

说一个我调了很久的才发现小错误,比如书2 2这样一个海报,如果你把这张海报的左右端点都记作2的话那就是个空区间了。

其实,这张海报覆盖的是第2块瓷砖。所以R++,2 3就表示第2块瓷砖的左右端点。

当然,如果不是我这个思路,就可能不会出现这个问题。

 

这个是跟着去年北大的ACM培训班上给的标程写的,指针满天飞,个人感觉代码不够简洁。

  1 //#define LOCAL

  2 #include <iostream>

  3 #include <algorithm>

  4 #include <cmath>

  5 using namespace std;

  6 

  7 int n;

  8 struct CPost

  9 {

 10     int L, R;

 11 }posters[10000 + 100];

 12 int x[20000 + 200];    //海报的端点瓷砖编号

 13 int hash[10000000 + 10];//hash[i]表示瓷砖i所处的离散化后的区间编号

 14 

 15 struct CNode

 16 {

 17     int L, R;

 18     bool bCovered;    //区间[L, R]是否已经完全覆盖

 19     CNode *pLeft, *pRight;

 20 }Tree[1000000];

 21 int nNodeCount = 0;

 22 int Mid(CNode* pRoot)

 23 {

 24     return (pRoot->L + pRoot->R) / 2;

 25 }

 26 void BuildTree(CNode *pRoot, int L, int R)

 27 {

 28     pRoot->L = L;

 29     pRoot->R = R;

 30     pRoot->bCovered = false;

 31     if(L == R)

 32         return;

 33     ++nNodeCount;

 34     pRoot->pLeft = Tree + nNodeCount;

 35     ++nNodeCount;

 36     pRoot->pRight = Tree + nNodeCount;

 37     BuildTree(pRoot->pLeft, L, (L+R)/2);

 38     BuildTree(pRoot->pRight, (L+R)/2+1, R);

 39 }

 40 bool Post(CNode *pRoot, int L, int R)

 41 {//插入一张覆盖区间[L, R]的海报,返回true则说明该区间是部分或者全部可见的

 42     if(pRoot->bCovered)

 43         return false;

 44     if(pRoot->L == L && pRoot->R == R)

 45     {

 46         pRoot->bCovered = true;

 47         return true;

 48     }

 49     bool bResult;

 50     if(R <= Mid(pRoot))

 51         bResult = Post(pRoot->pLeft, L, R);

 52     else if(L >= Mid(pRoot) + 1)

 53         bResult = Post(pRoot->pRight, L, R);

 54     else

 55     {

 56         bool b1 = Post(pRoot->pLeft, L, Mid(pRoot));

 57         bool b2 = Post(pRoot->pRight, Mid(pRoot) + 1, R);

 58         bResult = b1 || b2;

 59     }

 60     //要更新的节点的覆盖情况

 61     if(pRoot->pLeft->bCovered && pRoot->pRight->bCovered)

 62         pRoot->bCovered = true;

 63     return bResult;

 64 }

 65 

 66 int main(void)

 67 {

 68     #ifdef LOCAL

 69         freopen("2528in.txt", "r", stdin);

 70     #endif

 71     int t;

 72     int i, j, k;

 73     scanf("%d", &t);

 74     int nCaseNo = 0;

 75     while(t--)

 76     {

 77         ++nCaseNo;

 78         scanf("%d", &n);

 79         int nCount = 0;

 80         for(i = 0; i < n; ++i)

 81         {

 82             scanf("%d%d", &posters[i].L, &posters[i].R);

 83             x[nCount++] = posters[i].L;

 84             x[nCount++] = posters[i].R;

 85         }

 86         sort(x, x + nCount);

 87         nCount = unique(x, x + nCount) - x;//元素去重

 88         //将下面离散化

 89         int nIntervalNo = 0;

 90         for(i = 0; i < nCount; ++i)

 91         {

 92             hash[x[i]] = nIntervalNo;

 93             if(i < nCount - 1)

 94             {

 95                 if(x[i + 1] - x[i] == 1)

 96                     ++nIntervalNo;

 97                 else

 98                     nIntervalNo += 2;

 99             }

100         }

101 

102         BuildTree(Tree, 0, nIntervalNo);

103         int nSum = 0;

104         for(i = n - 1; i >= 0; --i)

105         {//从后往前遍历每个海报是否可见

106             if(Post(Tree, hash[posters[i].L], hash[posters[i].R]))

107                 ++nSum;

108         }

109         printf("%d\n", nSum);

110     }

111     return 0;

112 }
代码君

 

自己改成数组的写法,感觉可读性要好很多。

 1 #include <cstdio>

 2 #include <cstring>

 3 #include <algorithm>

 4 using namespace std;

 5 

 6 const int maxn = 100000 + 10;

 7 int covered[maxn << 2];

 8 int n, m, L[maxn], R[maxn], x[maxn];

 9 

10 bool post(int o, int L, int R, int qL, int qR)

11 {

12     if(covered[o]) return false;

13     if(qL == L && qR == R) { covered[o] = true; return true; }

14     int M = (L + R) / 2;

15     bool ok;

16     if(qR <= M) ok = post(o*2, L, M, qL, qR);

17     else if(qL > M) ok = post(o*2+1, M+1, R, qL, qR);

18     else

19     {

20         bool ok1 = post(o*2, L, M, qL, M);

21         bool ok2 = post(o*2+1, M+1, R, M+1, qR);

22         ok = ok1 || ok2;

23     }

24     if(covered[o*2] && covered[o*2+1]) covered[o] = true;

25     return ok;

26 }

27 

28 int main()

29 {

30     //freopen("in.txt", "r", stdin);

31 

32     int T; scanf("%d", &T);

33     while(T--)

34     {

35         scanf("%d", &n);

36         for(int i = 0; i < n; i++)

37         {

38             scanf("%d%d", &L[i], &R[i]);

39             R[i]++;

40             x[i*2] = L[i]; x[i*2+1] = R[i];

41         }

42         sort(x, x + n * 2);

43         m = unique(x, x + n * 2) - x;

44 

45         memset(covered, false, sizeof(covered));

46         int ans = 0;

47         for(int i = n - 1; i >= 0; i--)

48         {

49             int qL = lower_bound(x, x + m, L[i]) - x + 1;

50             int qR = lower_bound(x, x + m, R[i]) - x;

51             if(post(1, 1, m, qL, qR)) ans++;

52         }

53         printf("%d\n", ans);

54     }

55 

56     return 0;

57 }
代码君

 

还有一种方法就是正向模拟,用setv[o]来表示该节点被哪张海报覆盖,此时线段树的功能就是区间替换。

最后查询的时候就看这些节点被哪张海报覆盖,注意有的海报可能会覆盖多个节点,为了避免重复统计,可以用一个bool标记数组。

 1 #include <cstdio>

 2 #include <cstring>

 3 #include <algorithm>

 4 using namespace std;

 5 

 6 const int maxn = 20000 + 10;

 7 int setv[maxn << 2];

 8 bool is_cnt[maxn];

 9 

10 int L[maxn], R[maxn], x[maxn];

11 int n, m, qL, qR, v, ans;

12 

13 void pushdown(int o)

14 {

15     if(setv[o] >= 0)

16     {

17         setv[o*2] = setv[o*2+1] = setv[o];

18         setv[o] = -1;

19     }

20 }

21 

22 void update(int o, int L, int R)

23 {

24     if(qL <= L && qR >= R) { setv[o] = v; return; }

25     pushdown(o);

26     int M = (L + R) / 2;

27     if(qL <= M) update(o*2, L, M);

28     if(qR > M) update(o*2+1, M+1, R);

29 }

30 

31 void query(int o, int L, int R)

32 {

33     if(setv[o] >= 0)

34     {

35         if(!is_cnt[setv[o]]) { ans++; is_cnt[setv[o]] = true; }

36         return;

37     }

38     if(L == R) return;

39     int M = (L + R) / 2;

40     query(o*2, L, M);

41     query(o*2+1, M+1, R);

42 }

43 

44 int main()

45 {

46     //freopen("in.txt", "r", stdin);

47 

48     int T; scanf("%d", &T);

49     while(T--)

50     {

51         scanf("%d", &n);

52         for(int i = 0; i < n; i++)

53         {

54             scanf("%d%d", &L[i], &R[i]);

55             R[i]++;

56             x[i*2] = L[i]; x[i*2+1] = R[i];

57         }

58         sort(x, x + n * 2);

59         m = unique(x, x + n * 2) - x;

60 

61         memset(setv, -1, sizeof(setv));

62         memset(is_cnt, false, sizeof(is_cnt));

63 

64         for(int i = 0; i < n; i++)

65         {

66             v = i;

67             qL = lower_bound(x, x + m, L[i]) - x + 1;

68             qR = lower_bound(x, x + m, R[i]) - x;

69             update(1, 1, m - 1);

70         }

71         ans = 0;

72         query(1, 1, m - 1);

73         printf("%d\n", ans);

74     }

75 

76     return 0;

77 }
代码君

 

你可能感兴趣的:(post)