【ACM/ICPC2013】线段树题目集合(一)

前言:前一段时间在网上找了一个线段树题目列表,我顺着做了一些,今天我把做过的整理一下。感觉自己对线段树了解的还不是很深,自己的算法能力还要加强。光练代码能力还是不够的,要多思考。向队友学习,向大牛学习。

ZOJ1610

题目大意:先后对线段涂色,最后统计每种颜色出现的段数,为0则不输出。

分析:以点建树,每个节点有一个标记col,初始为-1,表示未涂过色。涂色时,若完全覆盖,则col为颜色编号,若覆盖一部分,则先将标记下放到左右儿子节点,再将标号标记为-2,表示此节点被部分覆盖过。最后从根节点开始往下遍历,若遇到节点col>-1的,直接将区间l到r标记为true,若col为-1,则返回,若为-2,则分别到左右儿子遍历。

参考代码:

 1 //
 2 //  ZOJ1610.cpp
 3 //  线段树
 4 //
 5 //  Created by TimmyXu on 13-8-11.
 6 //  Copyright (c) 2013年 TimmyXu. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 #include <cstdio>
11 #include <cstring>
12 #include <string>
13 #include <algorithm>
14 
15 const int maxn = 8000 + 10;
16 
17 struct node
18 {
19     int l,r,col;
20 }tree[maxn<<2];
21 
22 int n,le,ri,c,tmp,mark[maxn],color[maxn];
23 
24 void buildtree(int root,int l,int r)
25 {
26     tree[root].l = l;
27     tree[root].r = r;
28     tree[root].col = -1;
29     if (l < r)
30     {
31         buildtree(root<<1, l, (l+r)/2);
32         buildtree(root<<1|1, (l+r)/2+1, r);
33     }
34 }
35 
36 void update(int root,int l,int r,int c)
37 {
38     if (l <= tree[root].l && tree[root].r <= r)
39     {
40         tree[root].col = c;
41         return;
42     }
43     int mid = (tree[root].l+tree[root].r)/2;
44     if (tree[root].col > -2)
45     {
46         tree[root<<1].col = tree[root<<1|1].col = tree[root].col;
47         tree[root].col = -2;
48     }
49     if (l <= mid) update(root<<1, l, r, c);
50     if (mid < r) update(root<<1|1, l, r, c);
51 }
52 
53 void cnt(int root)
54 {
55     if (tree[root].col > -1)
56     {
57         for (int i = tree[root].l;i <= tree[root].r;i++)
58             mark[i] = tree[root].col;
59         return;
60     }
61     if (tree[root].col == -2)
62     {
63         cnt(root<<1);
64         cnt(root<<1|1);
65     }
66 }
67 
68 int main()
69 {
70     while (scanf("%d",&n)!=EOF)
71     {
72         buildtree(1,0,8000);
73         memset(mark,-1,sizeof(mark));
74         memset(color,0,sizeof(color));
75         while (n--)
76         {
77             scanf("%d%d%d",&le,&ri,&c);
78             update(1,le+1,ri,c);
79         }
80         cnt(1);
81         tmp = -1;
82         for (int i = 0;i <= 8000;i++)
83         {
84             if (tmp == mark[i]) continue;
85             tmp = mark[i];
86             if (tmp != -1) color[tmp]++;
87         }
88         for (int i = 0;i <= 8000;i++)
89             if (color[i])
90                 printf("%d %d
",i,color[i]);
91         printf("
");
92     }
93 }
View Code

POJ2777

题目大意:给两种操作,一种是对区间[a,b]涂色成c,一种是询问区间[a,b]之间不同颜色个数,在线的。由于给的区间直接是线段编号,所以以点建树,每个节点有一个标记col,初始都为颜色1。涂色时,若完全覆盖,则col为颜色编号,若覆盖一部分,则先将标记下放左右儿子,再将标号标记为-1,表示此节点被部分覆盖过。

查询时,

1、若给定区间覆盖当前节点区间,分两种情况:

       1)当前区间col>0,则将col标为true;

       2)当前区间col=-1,则分别到左右儿子遍历;

2、若给定区间覆盖当前节点的部分区间,需要遍历左右儿子,那么如果当前区间col>0,需要将col标记下放到左右儿子,再对左右儿子进行查询。

参考代码:

  1 //
  2 //  POJ2777.cpp
  3 //  线段树
  4 //
  5 //  Created by TimmyXu on 13-8-12.
  6 //  Copyright (c) 2013年 TimmyXu. All rights reserved.
  7 //
  8 
  9 #include <iostream>
 10 #include <cstdio>
 11 #include <cstring>
 12 #include <string>
 13 #include <algorithm>
 14 
 15 using namespace std;
 16 
 17 const int maxn = 100000 + 10,maxt = 30 + 10;
 18 
 19 struct node
 20 {
 21     int l,r,col;
 22 }tree[maxn<<2];
 23 
 24 int n,t,m,le,ri,c,ans;
 25 char ch;
 26 bool vis[maxt];
 27 
 28 void buildtree(int root,int l,int r)
 29 {
 30     tree[root].l = l;
 31     tree[root].r = r;
 32     tree[root].col = 1;
 33     if (l < r)
 34     {
 35         buildtree(root<<1, l, (l+r)/2);
 36         buildtree(root<<1|1, (l+r)/2+1, r);
 37     }
 38 }
 39 
 40 void update(int root,int l,int r,int c)
 41 {
 42     if (l <= tree[root].l && tree[root].r <= r)
 43     {
 44         tree[root].col = c;
 45         return;
 46     }
 47     if (tree[root].col > 0)
 48     {
 49         tree[root<<1].col = tree[root<<1|1].col = tree[root].col;
 50         tree[root].col = -1;
 51     }
 52     int mid = (tree[root].l+tree[root].r)/2;
 53     if (l <= mid) update(root<<1, l, r, c);
 54     if (mid < r) update(root<<1|1, l, r, c);
 55 }
 56 
 57 void cnt(int root,int l,int r)
 58 {
 59     if (l <= tree[root].l && tree[root].r <= r)
 60     {
 61         if (tree[root].col > 0)
 62             vis[tree[root].col] = true;
 63         else
 64         {
 65             cnt(root<<1,l,r);
 66             cnt(root<<1|1,l,r);
 67         }
 68         return;
 69     }
 70     if (tree[root].col > 0)
 71         tree[root<<1].col = tree[root<<1|1].col = tree[root].col;
 72     
 73     int mid = (tree[root].l+tree[root].r)/2;
 74     if (l <= mid) cnt(root<<1,l,r);
 75     if (mid < r) cnt(root<<1|1,l,r);
 76 }
 77 
 78 int main()
 79 {
 80     scanf("%d%d%d",&n,&t,&m);
 81     buildtree(1,1,n);
 82     while (m--)
 83     {   
 84         scanf("%c%c",&ch,&ch);
 85         if (ch == 'C')
 86         {
 87             scanf("%d%d%d",&le,&ri,&c);
 88             if (le > ri) swap(le,ri);       
 89             update(1,le,ri,c);
 90         }
 91         else
 92         {
 93             scanf("%d%d",&le,&ri);
 94             if (le > ri) swap(le,ri);
 95             memset(vis,false,sizeof(vis));
 96             cnt(1,le,ri);                  
 97             ans = 0;
 98             for (int i = 1;i <= t;i++)
 99                 ans+=vis[i];
100             printf("%d
",ans);
101         }
102     }
103 }
View Code

HDU1754

题目大意:两种操作,一个是把编号为a的点的值改为b,一个是询问[a,b]中最大值是多少。

分析:这题比较简单,以点建树,建树过程中建到叶子节点时读入每个点的初始值。更新时,若当前节点是叶子节点,直接更新,若是内部节点,则先递归更新,再用左右儿子的值来更新自己。

参考代码:

 1 //
 2 //  HDU1754.cpp
 3 //  线段树
 4 //
 5 //  Created by TimmyXu on 13-8-12.
 6 //  Copyright (c) 2013年 TimmyXu. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 #include <cstdio>
11 #include <cstring>
12 #include <string>
13 #include <algorithm>
14 
15 using namespace std;
16 
17 const int maxn = 200000 + 10;
18 
19 struct node
20 {
21     int l,r,maxs;
22 }tree[maxn<<2];
23 
24 int n,m,le,ri;
25 char ch;
26 
27 void buildtree(int root,int l,int r)
28 {
29     tree[root].l = l;
30     tree[root].r = r;
31     if (l == r)
32         scanf("%d",&tree[root].maxs);
33     else
34     {
35         buildtree(root<<1, l, (l+r)/2);
36         buildtree(root<<1|1, (l+r)/2+1, r);
37         tree[root].maxs = max(tree[root<<1].maxs,tree[root<<1|1].maxs);
38     }
39 }
40 
41 void update(int root,int pos,int c)
42 {
43     if (tree[root].l == tree[root].r)
44     {
45         tree[root].maxs = c;
46         return;
47     }
48     int mid = (tree[root].l+tree[root].r)/2;
49     if (pos <= mid) update(root<<1, pos, c);
50     else update(root<<1|1, pos, c);
51     tree[root].maxs = max(tree[root<<1].maxs,tree[root<<1|1].maxs);
52 }
53 
54 int getmax(int root,int l,int r)
55 {
56     if (l <= tree[root].l && tree[root].r <= r)
57         return tree[root].maxs;
58     int ans = 0,mid = (tree[root].l+tree[root].r)/2;
59     if (l <= mid) ans = max(ans,getmax(root<<1,l,r));
60     if (mid < r) ans = max(ans,getmax(root<<1|1,l,r));
61     return ans;
62 }
63 
64 int main()
65 {
66     while (scanf("%d%d",&n,&m)!=EOF)
67     {
68         buildtree(1,1,n);
69         while (m--)
70         {
71             scanf("%c%c%d%d",&ch,&ch,&le,&ri);
72             if (ch == 'Q')
73                 printf("%d
",getmax(1,le,ri));
74             else
75                 update(1,le,ri);
76         }
77     }
78 }
View Code

HDU1166

题目大意:和上一题类似,本题有三种操作,将编号a的值加b,将编号a的值减b(即加上-b),询问[a,b]和。

分析:以点建树,建树过程中遇到叶子节点读入初始值。更新时,首先将节点sum值加上b,若当前为叶子节点则返回,若为内部节点,则更新对应的左右儿子。询问时,若区间包含当前节点,则返回当前节点sum值,否则返回左儿子sum值+右儿子sum值。

参考代码:

 1 //
 2 //  HDU1166.cpp
 3 //  线段树
 4 //
 5 //  Created by TimmyXu on 13-8-12.
 6 //  Copyright (c) 2013年 TimmyXu. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 #include <cstdio>
11 #include <cstring>
12 #include <string>
13 #include <algorithm>
14 
15 using namespace std;
16 
17 const int maxn = 50000 + 10;
18 
19 struct node
20 {
21     int l,r,mid,sum;
22 }tree[maxn << 2];
23 
24 int t,n,le,ri;
25 char st[10];
26 
27 void buildtree(int root,int l,int r)
28 {
29     tree[root].l = l;
30     tree[root].r = r;
31     tree[root].mid = (l+r)/2;
32     if (l == r)
33     {
34         scanf("%d",&tree[root].sum);
35         return;
36     }
37     buildtree(root<<1, l, (l+r)/2);
38     buildtree(root<<1|1, (l+r)/2+1, r);
39     tree[root].sum = tree[root<<1].sum + tree[root<<1|1].sum;
40 }
41 
42 void update(int root,int p,int c)
43 {
44     tree[root].sum+=c;
45     if (tree[root].l == tree[root].r)
46         return;
47     if (p <= tree[root].mid) update(root<<1,p,c);
48     else update(root<<1|1,p,c);
49 }
50 
51 int getsum(int root,int l,int r)
52 {
53     if (l <= tree[root].l && tree[root].r <= r)
54         return tree[root].sum;
55     int ans = 0;
56     if (l <= tree[root].mid) ans += getsum(root<<1,l,r);
57     if (tree[root].mid < r) ans += getsum(root<<1|1,l,r);
58     return ans;
59 }
60 
61 int main()
62 {
63     scanf("%d",&t);
64     for (int o = 1;o <= t;o++)
65     {
66         printf("Case %d:
",o);
67         scanf("%d",&n);
68         if (n == 0) continue;
69         buildtree(1,1,n);
70         while (scanf("%s",st)!=EOF)
71         {
72             if (st[0] == 'E') break;
73             scanf("%d%d",&le,&ri);
74             if (st[0] == 'A')
75                 update(1,le,ri);
76             if (st[0] == 'S')
77                 update(1,le,-ri);
78             if (st[0] == 'Q')
79                 printf("%d
",getsum(1,le,ri));
80         }
81     }
82 }
View Code

HDU1698

题目大意:给定线段,初始值都为1,给定操作a,b,c,将[a,b]的值修改为c,c为1,2或3,最后询问线段所有数的和

分析:以点建树,初始标记col为1,更新时,若区间覆盖当前节点,则更新col为c,否则先将当前col下放到左右儿子,再标记为-1,表示当前节点被不止一种值覆盖。计算时,若当前节点col>0,则返回col*节点区间长度,否则返回左儿子值+右儿子值。

参考代码:

 1 //
 2 //  HDU1698.cpp
 3 //  线段树
 4 //
 5 //  Created by TimmyXu on 13-8-12.
 6 //  Copyright (c) 2013年 TimmyXu. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 #include <cstdio>
11 #include <cstring>
12 #include <string>
13 #include <algorithm>
14 
15 using namespace std;
16 
17 const int maxn = 100000;
18 
19 struct node
20 {
21     int l,r,len,mid,col;
22 }tree[maxn << 2];
23 
24 int t,n,m,le,ri,c;
25 
26 void buildtree(int root,int l,int r)
27 {
28     tree[root].l = l;
29     tree[root].r = r;
30     tree[root].len = r-l+1;
31     tree[root].mid = (l+r)/2;
32     tree[root].col = 1;
33     if (l < r)
34     {
35         buildtree(root<<1,l,(l+r)/2);
36         buildtree(root<<1|1, (l+r)/2+1, r);
37     }
38 }
39 
40 void update(int root,int l,int r,int c)
41 {
42     if (l <= tree[root].l && tree[root].r <= r)
43     {
44         tree[root].col = c;
45         return;
46     }
47     if (tree[root].col > 0)
48     {
49         tree[root<<1].col = tree[root<<1|1].col = tree[root].col;
50         tree[root].col = -1;
51     }
52     if (l <= tree[root].mid) update(root<<1,l,r,c);
53     if (tree[root].mid < r) update(root<<1|1, l, r, c);
54 }
55 
56 int getsum(int root)
57 {
58     if (tree[root].col > 0)
59         return tree[root].col * tree[root].len;
60     int ans = 0;
61     ans += getsum(root<<1);
62     ans += getsum(root<<1|1);
63     return ans;
64 }
65 
66 int main()
67 {
68     scanf("%d",&t);
69     for (int o = 1;o <= t;o++)
70     {
71         scanf("%d%d",&n,&m);
72         buildtree(1,1,n);
73         while (m--)
74         {
75             scanf("%d%d%d",&le,&ri,&c);
76             update(1,le,ri,c);
77         }
78         printf("Case %d: The total value of the hook is %d.
",o,getsum(1));
79     }
80 }
View Code

POJ3468

题目大意:给一列数,两种操作,一种是将[a,b]每个值加上c,另一种是询问[a,b]和。

分析:经典的区间修改区间询问题。以点建树,维护两个标记,add和sum,分别记录当前节点累加过的值和当前节点覆盖区间的值的和。更新时,若区间覆盖当前节点,则更新add和sum,否则,先用当前节点add值更新左右儿子的add和sum,再分别对左右儿子更新,更新完后再将当前节点sum值更新为左右儿子sum值之和。

参考代码:

  1 //
  2 //  POJ3468.cpp
  3 //  线段树
  4 //
  5 //  Created by TimmyXu on 13-8-12.
  6 //  Copyright (c) 2013年 TimmyXu. All rights reserved.
  7 //
  8 
  9 #include <iostream>
 10 #include <cstdio>
 11 #include <string>
 12 #include <cstring>
 13 #include <algorithm>
 14 
 15 using namespace std;
 16 
 17 const int maxn = 100000 + 10;
 18 
 19 struct node
 20 {
 21     int l,r,mid;
 22     long long len,add,sum;
 23 }tree[maxn<<2];
 24 
 25 int n,m,le,ri,c;
 26 char ch;
 27 
 28 void buildtree(int root,int l,int r)
 29 {
 30     tree[root].l = l;
 31     tree[root].r = r;
 32     tree[root].mid = (l+r)/2;
 33     tree[root].add = 0;
 34     tree[root].len = r-l+1;
 35     if (l == r)
 36     {
 37         scanf("%lld",&tree[root].sum);
 38         return;
 39     }
 40     buildtree(root<<1,l,(l+r)/2);
 41     buildtree(root<<1|1,(l+r)/2+1,r);
 42     tree[root].sum = tree[root<<1].sum + tree[root<<1|1].sum;
 43 }
 44 
 45 void update(int root,int l,int r,int c)
 46 {
 47     if (l <= tree[root].l && tree[root].r <= r)
 48     {
 49         tree[root].add += c;
 50         tree[root].sum += c*tree[root].len;
 51         return;
 52     }
 53     if (tree[root].add!=0)
 54     {
 55         tree[root<<1].add += tree[root].add;
 56         tree[root<<1].sum += tree[root].add*tree[root<<1].len;
 57         tree[root<<1|1].add += tree[root].add;
 58         tree[root<<1|1].sum += tree[root].add*tree[root<<1|1].len;
 59         tree[root].add = 0;
 60     }
 61     if (l <= tree[root].mid) update(root<<1,l,r,c);
 62     if (tree[root].mid < r) update(root<<1|1,l,r,c);
 63     tree[root].sum = tree[root<<1].sum + tree[root<<1|1].sum;
 64 }
 65 
 66 long long getsum(int root,int l,int r)
 67 {
 68     if (l <= tree[root].l && tree[root].r <= r)
 69         return tree[root].sum;
 70     if (tree[root].add!=0)
 71     {
 72         tree[root<<1].add += tree[root].add;
 73         tree[root<<1].sum += tree[root].add*tree[root<<1].len;
 74         tree[root<<1|1].add += tree[root].add;
 75         tree[root<<1|1].sum += tree[root].add*tree[root<<1|1].len;
 76         tree[root].add = 0;
 77     }
 78     long long ans = 0;
 79     if (l <= tree[root].mid) ans += getsum(root<<1,l,r);
 80     if (tree[root].mid < r) ans += getsum(root<<1|1,l,r);
 81     tree[root].sum = tree[root<<1].sum + tree[root<<1|1].sum;
 82     return ans;
 83 }
 84 
 85 int main()
 86 {
 87     scanf("%d%d",&n,&m);
 88     buildtree(1,1,n);
 89     while (m--)
 90     {
 91         scanf("%c%c%d%d",&ch,&ch,&le,&ri);
 92         if (ch == 'Q')
 93             printf("%lld
",getsum(1,le,ri));
 94         else
 95         {
 96             scanf("%d",&c);
 97             update(1,le,ri,c);
 98         }
 99     }
100 }
View Code

Ural 1019

题目大意:给一条线段,初始为白色,之后给一系列操作,将[a,b]涂为黑色或白色,最后统计出白色最长的一段,输出左右区间。

分析:由于区间特别大,所以先离散化。注意题目给的区间都是点,而涂的颜色是线段。所以不能以点建树,而是以区间建树,元区间为[a,a+1)。

整个线段初始为白色,所以在所有操作前加一条将整个区间涂为白色的操作。更新时,若区间覆盖当前节点区间,则更新col(白色为1,黑色为-1,被多种覆盖为0),否则将col下放到左右儿子,当前col更新为0,再更新左右儿子。查询时,若当前节点col为1,则将当前节点区间[l,r-1]改为true,为什么是r-1呢?因为我们要计算的是线段,也避免了原本不相邻离散化后相邻的情况出现。最后统计最长的为true的区间。

参考代码:

  1 //
  2 //  Ural1019.cpp
  3 //  线段树
  4 //
  5 //  Created by TimmyXu on 13-8-13.
  6 //  Copyright (c) 2013年 TimmyXu. All rights reserved.
  7 //
  8 
  9 #include <iostream>
 10 #include <cstdio>
 11 #include <cstring>
 12 #include <string>
 13 #include <algorithm>
 14 
 15 using namespace std;
 16 
 17 const int maxn = 10000 + 20,MAXX = 1000000000;
 18 
 19 struct node
 20 {
 21     int l,r,col;
 22 }tree[maxn<<2],data[maxn];
 23 
 24 struct node2
 25 {
 26     int p,num;
 27 }line[maxn<<1];
 28 
 29 int n,x,y,seg[maxn][3],cnt[maxn],top,tmp,ans,ansx,ansy;
 30 bool flag[maxn];
 31 char ch;
 32 
 33 bool cmp(const node2&p1,const node2&p2)
 34 {
 35     return p1.p < p2.p;
 36 }
 37 
 38 void buildtree(int root,int l,int r)
 39 {
 40     tree[root].l = l;
 41     tree[root].r = r;
 42     tree[root].col = 0;
 43     if (l+1<r)
 44     {
 45         buildtree(root<<1,l,(l+r)/2);
 46         buildtree(root<<1|1,(l+r)/2,r);
 47     }
 48 }
 49 
 50 void update(int root,int l,int r,int c)
 51 {
 52     if (l <= tree[root].l && tree[root].r <= r)
 53     {
 54         tree[root].col = c;
 55         return;
 56     }
 57     if (tree[root].col!=0)
 58     {
 59         tree[root<<1].col = tree[root<<1|1].col = tree[root].col;
 60         tree[root].col = 0;
 61     }
 62     int mid = (tree[root].l+tree[root].r)/2;
 63     if (l < mid) update(root<<1,l,r,c);
 64     if (mid < r) update(root<<1|1,l,r,c);
 65 }
 66 
 67 void paint(int root)
 68 {
 69     if (tree[root].col!=0)
 70     {
 71         if (tree[root].col == 1)
 72             for (int i = tree[root].l;i <= tree[root].r-1;i++)
 73                 flag[i] = true;
 74         return;
 75     }
 76     paint(root<<1);
 77     paint(root<<1|1);
 78 }
 79 
 80 int main()
 81 {
 82     scanf("%d",&n);
 83     memset(seg,0,sizeof(seg));
 84     memset(cnt,0,sizeof(cnt));
 85     for (int i = 1;i <= n;i++)
 86     {
 87         scanf("%d%d %c",&x,&y,&ch);
 88         seg[i][0] = x;
 89         seg[i][1] = y;
 90         if (ch == 'w') seg[i][2] = 1;
 91         else seg[i][2] = -1;
 92         line[i<<1].p = x;
 93         line[i<<1].num = -(i+1);
 94         line[i<<1|1].p = y;
 95         line[i<<1|1].num = i+1;
 96     }
 97     n++;
 98     seg[0][0] = 0;
 99     seg[0][1] = MAXX;
100     seg[0][2] = 1;
101     line[0].p = 0;
102     line[0].num = -1;
103     line[1].p = MAXX;
104     line[1].num = 1;
105     sort(line,line+n*2,cmp);
106     top = 1;
107     tmp = line[0].p;
108     cnt[1] = tmp;
109     for (int i = 0;i < 2*n;i++)
110     {
111         if (line[i].p!=tmp)
112         {
113             top++;
114             tmp = line[i].p;
115             cnt[top] = tmp;
116         }
117         if (line[i].num < 0)
118             seg[-line[i].num-1][0] = top;
119         else
120             seg[line[i].num-1][1] = top;
121     }
122     buildtree(1,1,top);
123     for (int i = 0;i < n;i++)
124         if (seg[i][0] != seg[i][1])
125             update(1,seg[i][0],seg[i][1],seg[i][2]);
126     memset(flag,false,sizeof(flag));
127     paint(1);
128     x = y = ans = 0;
129     for (int i = 1;i <= top;i++)
130     {
131         if (flag[i])
132             if (x == 0) x = i;
133         if (!flag[i])
134             if (x!=0)
135             {
136                 y = i;
137                 if (cnt[y]-cnt[x] > ans)
138                 {
139                     ans = cnt[y]-cnt[x];
140                     ansx = cnt[x];
141                     ansy = cnt[y];
142                 }
143                 x = y = 0;
144             }
145     } 
146     printf("%d %d
",ansx,ansy);
147 }
View Code

ZOJ2301

题目大意:跟上题差不多。这里不是涂线段而是涂小球,初始都为黑色。

分析:涂小球的时候给定的区间都是点,涂的也是点,为了避免原本不相邻离散化后却相邻的情况出现,我们把题目给定的区间的右端点+1,再以区间建树。最后输出结果时再把右区间-1即可。

参考代码:

  1 //
  2 //  ZOJ2301.cpp
  3 //  线段树
  4 //
  5 //  Created by TimmyXu on 13-8-13.
  6 //  Copyright (c) 2013年 TimmyXu. All rights reserved.
  7 //
  8 
  9 
 10 #include <iostream>
 11 #include <cstdio>
 12 #include <cstring>
 13 #include <string>
 14 #include <algorithm>
 15 
 16 using namespace std;
 17 
 18 const int maxn = 20000 + 20;
 19 
 20 struct node
 21 {
 22     int l,r,col;
 23 }tree[maxn<<2],data[maxn];
 24 
 25 struct node2
 26 {
 27     int p,num;
 28 }line[maxn<<1];
 29 
 30 int n,x,y,seg[maxn][3],cnt[maxn],top,tmp,ans,ansx,ansy;
 31 bool flag[maxn];
 32 char ch;
 33 
 34 bool cmp(const node2&p1,const node2&p2)
 35 {
 36     return p1.p < p2.p;
 37 }
 38 
 39 void buildtree(int root,int l,int r)
 40 {
 41     tree[root].l = l;
 42     tree[root].r = r;
 43     tree[root].col = -1;
 44     if (l+1<r)
 45     {
 46         buildtree(root<<1,l,(l+r)/2);
 47         buildtree(root<<1|1,(l+r)/2,r);
 48     }
 49 }
 50 
 51 void update(int root,int l,int r,int c)
 52 {
 53     if (l <= tree[root].l && tree[root].r <= r)
 54     {
 55         tree[root].col = c;
 56         return;
 57     }
 58     if (tree[root].col!=0)
 59     {
 60         tree[root<<1].col = tree[root<<1|1].col = tree[root].col;
 61         tree[root].col = 0;
 62     }
 63     int mid = (tree[root].l+tree[root].r)/2;
 64     if (l < mid) update(root<<1,l,r,c);
 65     if (mid < r) update(root<<1|1,l,r,c);
 66 }
 67 
 68 void paint(int root)
 69 {
 70     if (tree[root].col!=0)
 71     {
 72         if (tree[root].col == 1)
 73             for (int i = tree[root].l;i <= tree[root].r-1;i++)
 74                 flag[i] = true;
 75         return;
 76     }
 77     paint(root<<1);
 78     paint(root<<1|1);
 79 }
 80 
 81 int main()
 82 {
 83     while(scanf("%d",&n)!=EOF)
 84     {
 85     memset(seg,0,sizeof(seg));
 86     memset(cnt,0,sizeof(cnt));
 87     for (int i = 0;i < n;i++)
 88     {
 89         scanf("%d%d %c",&x,&y,&ch);
 90         y++;
 91         seg[i][0] = x;
 92         seg[i][1] = y;
 93         if (ch == 'w') seg[i][2] = 1;
 94         else seg[i][2] = -1;
 95         line[i<<1].p = x;
 96         line[i<<1].num = -(i+1);
 97         line[i<<1|1].p = y;
 98         line[i<<1|1].num = i+1;
 99     }
100     sort(line,line+n*2,cmp);
101     top = 1;
102     tmp = line[0].p;
103     cnt[1] = tmp;
104     for (int i = 0;i < 2*n;i++)
105     {
106         if (line[i].p!=tmp)
107         {
108             top++;
109             tmp = line[i].p;
110             cnt[top] = tmp;
111         }
112         if (line[i].num < 0)
113             seg[-line[i].num-1][0] = top;
114         else
115             seg[line[i].num-1][1] = top;
116     }
117     buildtree(1,1,top);
118     for (int i = 0;i < n;i++)
119         update(1,seg[i][0],seg[i][1],seg[i][2]);
120     memset(flag,false,sizeof(flag));
121     paint(1);
122     x = y = ans = 0;
123     for (int i = 1;i <= top;i++)
124     {
125         if (flag[i])
126             if (x == 0) x = i;
127         if (!flag[i])
128             if (x!=0)
129             {
130                 y = i;
131                 if (cnt[y]-cnt[x] > ans)
132                 {
133                     ans = cnt[y]-cnt[x];
134                     ansx = cnt[x];
135                     ansy = cnt[y]-1;
136                 }
137                 x = y = 0;
138             }
139     }
140     if (ans == 0)
141         printf("Oh, my god
");
142     else
143         printf("%d %d
",ansx,ansy);
144     }
145 }
View Code

POJ3264

题目大意:给一列数,给一些询问,问[a,b]内最大值-最小值是多少

分析:比较简单的题目。以点建树,建树时,遇到叶子节点则读入,max = min = 读入值,否则用左右儿子的最大最小值来更新当前节点的最大最小值。查询就不多说了。

本题还有一种比较简便的RMQ做法,我用RMQ的ST算法也写了一遍,下面代码一起贴上。

参考代码1(线段树):

 1 //
 2 //  POJ3264.cpp
 3 //  线段树
 4 //
 5 //  Created by TimmyXu on 13-8-14.
 6 //  Copyright (c) 2013年 TimmyXu. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 #include <cstdio>
11 #include <cstring>
12 #include <string>
13 #include <algorithm>
14 #include <climits>
15 
16 using namespace std;
17 
18 const int maxn = 50000 + 10;
19 
20 struct node
21 {
22     int l,r,mid,m1,m2;
23 }tree[maxn<<2];
24 
25 int n,q,x,y,m1,m2;
26 
27 void buildtree(int root,int l,int r)
28 {
29     tree[root].l = l;
30     tree[root].r = r;
31     tree[root].mid = (l+r)/2;
32     if (l == r)
33     {
34         scanf("%d",&tree[root].m1);
35         tree[root].m2 = tree[root].m1;
36         return;
37     }
38     buildtree(root<<1,l,(l+r)/2);
39     buildtree(root<<1|1,(l+r)/2+1,r);
40     tree[root].m1 = max(tree[root<<1].m1,tree[root<<1|1].m1);
41     tree[root].m2 = min(tree[root<<1].m2,tree[root<<1|1].m2);
42 }
43 
44 int getmax(int root,int l,int r)
45 {
46     if (l <= tree[root].l && tree[root].r <= r)
47         return tree[root].m1;
48     int ans = 0;
49     if (l <= tree[root].mid) ans = max(ans,getmax(root<<1,l,r));
50     if (tree[root].mid < r) ans = max(ans,getmax(root<<1|1,l,r));
51     return ans;
52 }
53 
54 int getmin(int root,int l,int r)
55 {
56     if (l <= tree[root].l && tree[root].r <= r)
57         return tree[root].m2;
58     int ans = INT_MAX;
59     if (l <= tree[root].mid) ans = min(ans,getmin(root<<1,l,r));
60     if (tree[root].mid < r) ans = min(ans,getmin(root<<1|1,l,r));
61     return ans;
62 }
63 
64 int main()
65 {
66     scanf("%d%d",&n,&q);
67     buildtree(1,1,n);
68     while (q--)
69     {
70         scanf("%d%d",&x,&y);
71         m1 = getmax(1,x,y);
72         m2 = getmin(1,x,y);
73         printf("%d
",m1-m2);
74     }
75 }
View Code

参考代码2(RMQ):

 1 //
 2 //  POJ3264(RMQ).cpp
 3 //  线段树
 4 //
 5 //  Created by TimmyXu on 13-8-14.
 6 //  Copyright (c) 2013年 TimmyXu. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 #include <cstdio>
11 #include <cstring>
12 #include <string>
13 #include <algorithm>
14 #include <cmath>
15 
16 using namespace std;
17 
18 const int maxn = 50000 + 10;
19 
20 int n,q,data[maxn],x,y,m1,m2,k,f1[maxn][20],f2[maxn][20];
21 
22 int main()
23 {
24     scanf("%d%d",&n,&q);
25     memset(f1,0,sizeof(f1));
26     memset(f2,0,sizeof(f2));
27     for (int i = 1;i <= n;i++)
28     {
29         scanf("%d",&f1[i][0]);
30         f2[i][0] = f1[i][0];
31     }
32     k = trunc(log(double(n))/log(double(2)));
33     for (int j = 1;j <= k;j++)
34         for (int i = 1;i <= n-(1<<j)+1;i++)
35         {
36             f1[i][j] = max(f1[i][j-1],f1[i+(1<<(j-1))][j-1]);
37             f2[i][j] = min(f2[i][j-1],f2[i+(1<<(j-1))][j-1]);
38         }
39     while (q--)
40     {
41         scanf("%d%d",&x,&y);
42         k = trunc(log(double(y-x+1))/log(double(2)));
43         m1 = max(f1[x][k],f1[y-(1<<k)+1][k]);
44         m2 = min(f2[x][k],f2[y-(1<<k)+1][k]);
45         printf("%d
",m1-m2);
46     }
47 }
View Code

POJ1151

题目大意:题意很简单,求矩形的面积并。

分析:对y坐标进行离散化,以区间建树。将矩形分成两个线段,设标记flag,flag=1表示为左线段,flag=-1表示为右线段。排序所有线段,从左到右插入。线段树每个节点有两个标记,cover和len,cover>0表示该节点对应y坐标范围仍然被覆盖,len表示该节点覆盖的y坐标的长度。更新时,若区间覆盖当前节点,则cover+=flag,更新当前节点的len,否则更新左右节点,再更新当前节点len。更新len时,若当前节点cover>0,则len为当前区间对应y的长度,否则len为左儿子len+右儿子len。每插入一个线段,总面积+=根节点的len*下一个线段和当前线段的x之差。

(我知道我说的很模糊,可能我理解的不太深刻吧,不能清楚的表达出来。。)

参考代码:

  1 //
  2 //  POJ1151(2).cpp
  3 //  线段树
  4 //
  5 //  Created by TimmyXu on 13-8-16.
  6 //  Copyright (c) 2013年 TimmyXu. All rights reserved.
  7 //
  8 
  9 #include <iostream>
 10 #include <cstdio>
 11 #include <cstring>
 12 #include <string>
 13 #include <algorithm>
 14 #define FILL(a,b) memset(a,b,sizeof(a));
 15 
 16 using namespace std;
 17 
 18 const int maxn = 100 + 10;
 19 
 20 int n,o,tot,len,a,b;
 21 
 22 double y[maxn<<1],x1,y1,x2,y2,z,ans;
 23 
 24 struct LINE
 25 {
 26     double x,y1,y2;
 27     int flag;
 28     bool operator<(const LINE&p)const
 29     {
 30         return x<p.x;
 31     }
 32 }line[maxn<<1];
 33 
 34 struct node
 35 {
 36     int l,r,cover;
 37     double len;
 38 }tree[maxn<<4];
 39 
 40 void buildtree(int root,int l,int r)
 41 {
 42     tree[root].l = l;
 43     tree[root].r = r;
 44     tree[root].cover = tree[root].len = 0;
 45     if (l+1<r)
 46     {
 47         buildtree(root<<1, l, (l+r)/2);
 48         buildtree(root<<1|1, (l+r)/2, r);
 49     }
 50 }
 51 
 52 int findp(double x)
 53 {
 54     int l = 1,r = len,mid;
 55     while (l<r)
 56     {
 57         mid = (l+r)/2;
 58         if (y[mid] == x) return mid;
 59         if (y[mid]<x)l=mid+1;
 60         else r=mid;
 61     }
 62     return l;
 63 }
 64 
 65 void up(int root)
 66 {
 67     if (tree[root].cover>0)
 68     {
 69         tree[root].len = y[tree[root].r]-y[tree[root].l];
 70         return;
 71     }
 72     if (tree[root].l+1==tree[root].r)
 73     {
 74         tree[root].len = 0;
 75         return;
 76     }
 77     tree[root].len = tree[root<<1].len+tree[root<<1|1].len;
 78 }
 79 
 80 void update(int root,int l,int r,int flag)
 81 {
 82     if (l<=tree[root].l && tree[root].r <= r)
 83     {
 84         tree[root].cover += flag;
 85         up(root);
 86         return;
 87     }
 88     int mid = (tree[root].l+tree[root].r)/2;
 89     if (l<mid)
 90         update(root<<1,l,r,flag);
 91     if (mid<r)
 92         update(root<<1|1,l,r,flag);
 93     up(root);
 94     return;
 95 }
 96 
 97 int main()
 98 {
 99     o = 0;
100     while (scanf("%d",&n),n)
101     {
102         FILL(y,0);
103         tot = len = ans = 0;
104         for (int i = 1;i <= n;i++)
105         {
106             scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
107             tot++;
108             y[tot] = y1;
109             line[tot].x = x1;
110             line[tot].y1 = y1;
111             line[tot].y2 = y2;
112             line[tot].flag = 1;
113             
114             tot++;
115             y[tot] = y2;
116             line[tot].x = x2;
117             line[tot].y1 = y1;
118             line[tot].y2 = y2;
119             line[tot].flag = -1;
120         }
121         len = 1;
122         sort(y+1,y+1+tot);
123         for (int i = 2;i <= tot;i++)
124             if (y[i]!=y[i-1])
125                 y[++len] = y[i];
126         sort(line+1,line+1+tot);
127         buildtree(1,1,len);
128         for (int i = 1;i < tot;i++)
129         {
130             a = findp(line[i].y1);
131             b = findp(line[i].y2);
132             update(1,a,b,line[i].flag);
133             ans += (line[i+1].x-line[i].x)*tree[1].len;
134         }
135         printf("Test case #%d
Total explored area: %.2f

",++o,ans);
136     }
137 }
View Code

POJ1177

题目大意:求区间周长并。

分析:本题和求面积并类似,但是稍微难一点。建树时,要维护5个标记,lb,rb表示该节点是否被覆盖,被覆盖则为1,否则为0;cnt表示该节点覆盖了几段分开的y,用于求解x长度计算的次数;len表示当前区间包含的y的长度;cover同上。离散化方式跟上题一样。先将第一条线段插入,计算出根节点的len和cnt,然后从第二条线段循环到最后一条,每次更新时,若区间覆盖当前节点,则cover+=flag,否则更新左右节点。同样,每次更新完后要更新当前节点的lb,rb,len,cnt。若cover>0,表示当前节点整个都被覆盖了,那么lb=rb=cnt=1,len为当前节点对应y的长度;否则lb为左儿子lb,rb为右儿子rb,cnt为左儿子cnt+右儿子cnt,len为左儿子len+右儿子len,若左儿子rb和右儿子lb均为1,则cnt-1,因为表示左右儿子区间在中间是连起来了,所以cnt多算了一个。最后算周长时和面积并差不多。

(貌似我说的更不清楚了。。)

参考代码:

  1 //
  2 //  POJ1177.cpp
  3 //  线段树
  4 //
  5 //  Created by TimmyXu on 13-8-16.
  6 //  Copyright (c) 2013年 TimmyXu. All rights reserved.
  7 //
  8 
  9 #include <iostream>
 10 #include <cstdio>
 11 #include <cstring>
 12 #include <string>
 13 #include <algorithm>
 14 #define FILL(a) memset(a,0,sizeof(a));
 15 #define lc root<<1
 16 #define rc root<<1|1
 17 
 18 using namespace std;
 19 
 20 const int maxn = 10000 + 10;
 21 
 22 class LINE{
 23 public:
 24     int x,y1,y2,flag;
 25     LINE(){};
 26     LINE(int a,int b,int c,int d):x(a),y1(b),y2(c),flag(d){}
 27     bool operator<(const LINE&p) const
 28     {
 29         if (x!=p.x)
 30             return x<p.x;
 31         return flag>p.flag;
 32     }
 33 }line[maxn];
 34 
 35 struct node
 36 {
 37     int l,r,lb,rb,cover,cnt,len;
 38 }tree[maxn<<2];
 39 
 40 int n,y[maxn],top,len,ans,x1,y1,x2,y2,tmplen,tmpcnt,a,b;
 41 
 42 int abs(int x)
 43 {
 44     return x<0?-x:x;
 45 }
 46 
 47 void build(int root,int l,int r)
 48 {
 49     tree[root].l = l;
 50     tree[root].r = r;
 51     tree[root].lb = tree[root].rb = tree[root].cover = tree[root].cnt = tree[root].len = 0;
 52     if (l+1<r)
 53     {
 54         build(lc,l,(l+r)/2);
 55         build(rc,(l+r)/2,r);
 56     }
 57 }
 58 
 59 void up(int root)
 60 {
 61     if (tree[root].cover>0)
 62     {
 63         tree[root].cnt = 1;
 64         tree[root].lb = tree[root].rb = 1;
 65         tree[root].len = y[tree[root].r]-y[tree[root].l];
 66         return;
 67     }
 68     if (tree[root].l+1==tree[root].r)
 69     {
 70         tree[root].len = tree[root].cnt = 0;
 71         tree[root].lb = tree[root].rb = 0;
 72         return;
 73     }
 74     tree[root].lb = tree[lc].lb;
 75     tree[root].rb = tree[rc].rb;
 76     tree[root].len = tree[lc].len+tree[rc].len;
 77     tree[root].cnt = tree[lc].cnt+tree[rc].cnt;
 78     if (tree[lc].rb&&tree[rc].lb)
 79         tree[root].cnt--;
 80 }
 81 
 82 void update(int root,int l,int r,int flag)
 83 {
 84     if (l <= tree[root].l && tree[root].r <= r)
 85     {
 86         tree[root].cover += flag;
 87         up(root);
 88         return;
 89     }
 90     if (l < tree[lc].r) update(lc,l,r,flag);
 91     if (tree[rc].l < r) update(rc,l,r,flag);
 92     up(root);
 93 }
 94 
 95 int main()
 96 {
 97     while(scanf("%d",&n)!=EOF)
 98     {
 99     FILL(y);
100     top = len = ans = tmplen = 0;
101     for (int i = 1;i <= n;i++)
102     {
103         scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
104         y[top] = y1;
105         line[top]= *new LINE(x1,y1,y2,1);
106         top++;
107         
108         y[top] = y2;
109         line[top]= *new LINE(x2,y1,y2,-1);
110         top++;
111     }
112     sort(line,line+top);
113     sort(y,y+top);
114     len = unique(y,y+top)-y;
115     build(1,0,len-1);
116     a = lower_bound(y, y+len, line[0].y1)-y;
117     b = lower_bound(y, y+len, line[0].y2)-y;
118     update(1,a,b,line[0].flag);
119     tmplen = ans = tree[1].len;
120     tmpcnt = tree[1].cnt;
121     for (int i = 1;i < top;i++)
122     {
123         a = lower_bound(y, y+len, line[i].y1)-y;
124         b = lower_bound(y, y+len, line[i].y2)-y;
125         update(1,a,b,line[i].flag);
126         ans += abs(tree[1].len-tmplen);
127         ans += tmpcnt*2*(line[i].x-line[i-1].x);
128         tmplen = tree[1].len;
129         tmpcnt = tree[1].cnt;
130     }
131     printf("%d
",ans);
132     }
133 }
View Code

POJ3277

题目大意:同一水平线上不同高度的矩形,求面积并。

分析:简化版的矩形面积并。离散化x,设节点标记cover表示当前节点最大高度。由y从小到大对每个操作排序,否则更新的时候小的会覆盖大的,模拟一下便知。更新时,将y插入[x1,x2](离散化后)时,若区间覆盖当前节点,则将y与当前cover比较,求最大值。否则将当前cover下放到左右儿子,更新左右儿子。统计时,若当前区间cover>0,则返回区间差*cover,否则返回左右儿子的面积和。

参考代码:

  1 //
  2 //  POJ3277.cpp
  3 //  线段树
  4 //
  5 //  Created by TimmyXu on 13-8-17.
  6 //  Copyright (c) 2013年 TimmyXu. All rights reserved.
  7 //
  8 
  9 #include <iostream>
 10 #include <cstdio>
 11 #include <cstring>
 12 #include <string>
 13 #include <algorithm>
 14 
 15 #define FILL(a) memset(a,0,sizeof(a));
 16 #define lc root<<1
 17 #define rc root<<1|1
 18 
 19 using namespace std;
 20 
 21 const int maxn = 80000 + 10;
 22 
 23 struct node
 24 {
 25     int l,r;
 26     long long cover;
 27 }tree[maxn<<2];
 28 
 29 struct node2
 30 {
 31     long long x1,x2,y;
 32     bool operator<(const node2&p)const
 33     {
 34         return y<p.y;
 35     }
 36 }seg[maxn];
 37 
 38 int n,tot,a,b;
 39 long long ans,tmp,x[maxn];
 40 
 41 void build(int root,int l,int r)
 42 {
 43     tree[root].l = l;
 44     tree[root].r = r;
 45     tree[root].cover = 0;
 46     if (l+1<r)
 47     {
 48         build(lc,l,(l+r)/2);
 49         build(rc,(l+r)/2,r);
 50     }
 51 }
 52 
 53 void update(int root,int l,int r)
 54 {
 55     if (l <= tree[root].l && tree[root].r <= r)
 56     {
 57         tree[root].cover = max(tree[root].cover,tmp);
 58         return;
 59     }
 60     if (tree[root].l+1<tree[root].r)
 61     {
 62         tree[lc].cover = max(tree[lc].cover,tree[root].cover);
 63         tree[rc].cover = max(tree[rc].cover,tree[root].cover);
 64         tree[root].cover = 0;
 65         if (l<tree[lc].r) update(lc,l,r);
 66         if (tree[rc].l<r) update(rc,l,r);
 67     }
 68 }
 69 
 70 long long getsum(int root)
 71 {
 72     if (tree[root].cover > 0)
 73         return tree[root].cover*(x[tree[root].r]-x[tree[root].l]);
 74     long long ans = 0;
 75     if (tree[root].l+1<tree[root].r)
 76     {
 77         ans += getsum(lc);
 78         ans += getsum(rc);
 79     }
 80     return ans;
 81 }
 82 
 83 int main()
 84 {
 85     scanf("%d",&n);
 86     FILL(seg);
 87     tot = 0;
 88     for (int i = 0;i < n;i++)
 89     {
 90         scanf("%lld%lld%lld",&seg[i].x1,&seg[i].x2,&seg[i].y);
 91         x[tot++] = seg[i].x1;
 92         x[tot++] = seg[i].x2;
 93     }
 94     sort(seg,seg+n);
 95     sort(x,x+tot);
 96     tot = unique(x,x+tot)-x;
 97     build(1,0,tot-1);
 98     for (int i = 0;i < n;i++)
 99     {
100         a = lower_bound(x, x+tot, seg[i].x1)-x;
101         b = lower_bound(x, x+tot, seg[i].x2)-x;
102         tmp = seg[i].y;
103         update(1,a,b);
104     }
105     ans = getsum(1);
106     printf("%lld
",ans);
107 }
View Code

*HDU1394

题目大意:给一列n的某种排列,每次将第一个数移到最后一个,操作n-1次,这样加上初始序列一共有n种不同的序列,问这n种序列中逆序数最小的是多少。

分析:为什么这题我加了*呢,因为这道被标记为线段树的题目我是用树状数组写的。。而且目前我还不会线段树的解法。。

用树状数组时,首先,对于原序列,从后往前插入,计算f[i],表示当前位置之后的逆序数。再对每个f[i]累加,算出初始ans。

每次将第一个数移到最后时,产生的新的逆序数=上一个序列逆序数-f[i]+n-f[i]-1;

这个是显而易见的,那么移第一个数时这是成立的,移后面的数呢?我们再设一个数组g[i],表示当前这个数前面比它小的数,每次移动i时,f[i]+=g[i],i之前比它小的数都移到后面的话,也被加到f[i]里面了,这样循环一遍,就能统计出每种序列的逆序数,取最小值即可。

参考代码:

 1 //
 2 //  HDU1394.cpp
 3 //  线段树
 4 //
 5 //  Created by TimmyXu on 13-8-17.
 6 //  Copyright (c) 2013年 TimmyXu. All rights reserved.
 7 //
 8 
 9 #include <iostream>
10 #include <cstdio>
11 #include <cstring>
12 #include <string>
13 #include <algorithm>
14 
15 #define FILL(a) memset(a,0,sizeof(a));
16 
17 using namespace std;
18 
19 const int maxn = 5000 + 10;
20 
21 int n,c[maxn],c2[maxn],f[maxn],g[maxn],data[maxn],ans,tmp;
22 
23 inline int lowbit(int x)
24 {
25     return x&(-x);
26 }
27 
28 void add(int x)
29 {
30     for (int i = x;i < maxn;i+=lowbit(i))
31         c[i]++;
32 }
33 
34 int sum(int x)
35 {
36     int tot = 0;
37     for (int i = x;i > 0;i-=lowbit(i))
38         tot+=c[i];
39     return tot;
40 }
41 
42 void add2(int x)
43 {
44     for (int i = x;i < maxn;i+=lowbit(i))
45         c2[i]++;
46 }
47 
48 int sum2(int x)
49 {
50     int tot = 0;
51     for (int i = x;i > 0;i-=lowbit(i))
52         tot+=c2[i];
53     return tot;
54 }
55 
56 int main()
57 {
58     while (scanf("%d",&n)!=EOF)
59     {
60         FILL(c);
61         FILL(c2);
62         FILL(f);
63         FILL(g);
64         for (int i = 1;i <= n;i++)
65         {
66              scanf("%d",&data[i]);
67             data[i]++;
68         }
69         ans = 0;
70         for (int i = n;i >= 1;i--)
71         {
72             add(data[i]+1);
73             f[i] = sum(data[i]);
74             ans+=f[i];
75         }
76         tmp = ans;
77         for (int i = 1;i < n;i++)
78         {
79             g[i] = sum2(data[i]-1);
80             f[i]+=g[i];
81             tmp = tmp-f[i]+n-f[i]-1;
82             ans = min(ans,tmp);
83             add2(data[i]);
84         }
85         printf("%d
",ans);
86     }
87 }
View Code

总结:话说做了这么多天线段树,才做了大概1/3都不到。。刷题真是累死人。。

原文地址:https://www.cnblogs.com/xsf72006/p/3266144.html