专题训练之树状数组

一曲冷凌霜 提交于 2020-03-30 09:18:20

推荐几个博客:https://blog.csdn.net/int64ago/article/details/7429868搞懂树状数组

https://blog.csdn.net/z309241990/article/details/9615259区间修改

https://blog.csdn.net/whereisherofrom/article/details/78922383完整版+题集

http://www.cnblogs.com/wuyiqi/archive/2011/12/25/2301071.html二进制思想求第k大数

http://www.cnblogs.com/oa414/archive/2011/07/21/2113234.html二分/二进制思想求第k大数

 

一维树状数组模板(区间求和、单点修改)

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int maxn=1e5+10;
 6 int bit[maxn],n;
 7 
 8 int lowbit(int x)
 9 {
10     return x&(-x);
11 }
12 
13 void add(int k,int num)
14 {
15     while ( k<=n ) {
16         bit[k]+=num;
17         k+=lowbit(k);
18     }
19 }
20 
21 int sum(int k)
22 {
23     int s=0;
24     while ( k ) {
25         s+=bit[k];
26         k-=lowbit(k);
27     }
28     return s;
29 }
树状数组模板(区间求和+单点修改)

 

二维树状数组模板(区间求和、单点修改)

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int maxn=1500;
 6 int bit[maxn][maxn],n;
 7 
 8 int lowbit(int x)
 9 {
10     return x&(-x);
11 }
12 
13 void add(int x,int y,int num)
14 {
15     for ( int i=x;i<=n;i+=lowbit(i) ) {
16         for ( int j=y;j<=n;j+=lowbit(j) ) {
17             bit[i][j]+=num;
18         }
19     }
20 }
21 
22 int sum(int x,int y)
23 {
24     int s=0;
25     for ( int i=x;i>0;i-=lowbit(i) ) {
26         for ( int j=y;j>0;j-=lowbit(j) ) {
27             s+=bit[i][j];
28         }
29     }
30     return s;
31 }
32 
33 int SUM(int x1,int y1,int x2,int y2)
34 {
35     return sum(x2,y2)-sum(x2,y1-1)-sum(x1-1,y2)+sum(x1-1,y1-1);
36 }
二维数组数组模板(区间求和、单点修改)

 

1.(HDOJ1541)http://acm.hdu.edu.cn/showproblem.php?pid=1541

题意:给出N个恒星的坐标(按照y从小到大,若y相同则按照x从小到大输入)。每个恒星的等级于所有在他左下方恒星的数量相等。输出0到n-1等级恒星的数量

分析:因为题目的顺序是按照y从小到大的顺序输入的。所有可以想象把二维图压缩成一维(或者说是投影到一维平面)。这时候按照读入顺序一步步将横坐标更新进树状数组中,同时对于第i个恒星(xi,yi)的等级为sum(xi),即所有x<=xi数的数量之和。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int maxn=1e5+10;
 6 int bit[maxn],n,cnt[maxn];
 7 struct node{
 8     int x;
 9     int y;
10 }arr[maxn];
11 
12 int lowbit(int x)
13 {
14     return x&(-x);
15 }
16 
17 void add(int k,int num)
18 {
19     while ( k<=n ) {
20         bit[k]+=num;
21         k+=lowbit(k);
22     }
23 }
24 
25 int sum(int k)
26 {
27     int s=0;
28     while ( k ) {
29         s+=bit[k];
30         k-=lowbit(k);
31     }
32     return s;
33 }
34 
35 int main()
36 {
37     int i,j,k,x,y,rk,N;
38     while ( scanf("%d",&N)!=EOF ) {
39         memset(cnt,0,sizeof(cnt));
40         memset(bit,0,sizeof(bit));
41         n=0;
42         for ( i=0;i<N;i++ ) {
43             scanf("%d%d",&arr[i].x,&arr[i].y);
44             n=max(n,arr[i].x);
45         }
46         n++;
47         for ( i=0;i<N;i++ ) {
48             x=arr[i].x+1;
49             y=arr[i].y+1;
50             rk=sum(x);
51             cnt[rk]++;
52             add(x,1);
53         }
54         for ( i=0;i<N;i++ ) printf("%d\n",cnt[i]);
55     }
56     return 0;
57 }
HDOJ1541

注意两点:A.树状数组的n(横坐标的最大值)和恒星数量的N不是同一个N 

B.对于树状数组的题目要特别注意是否会有0这个量。树状数组无法处理0.所有这题将所有横坐标都+1了

 

2.(POJ1990)http://poj.org/problem?id=1990

题意:有N头牛,每头牛都有一个横坐标xi和音量yi。对于任意两头牛它们之间交流需要耗费max(yi,yj)*abs(xi-xj),即音量中大的值乘以它们之间的距离。现在求所有牛相互交谈需要耗费多少。

分析:因为音量需要取较大的那个,所以我们对音量进行从小到大的排序,这样就可以保证每次计算时都可以选当前那个较大的音量进行计算。而对当前那头牛的距离xi与其他牛距离的关系讨论时,我们则需要分类讨论,分成x<xi和x>xi的进行考虑。我们利用两个树状数组一个存数量(即在距离x那个地方+1表示该位置有一头牛),记作num[];而另一个树状则保存距离(即在x的地方+x),记做bit[]。那么对于第i头牛(均为排序后,以下同)与前面那i-1头牛的消耗为yi*坐标差之和,坐标差之和由两部分组成,x*(小于x坐标的牛的数目)-(小于x坐标的牛的距离之和),另一部分为(大于x坐标的牛的距离之和)-x*(小于x坐标的牛的数量)

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 typedef long long ll;
 6 const ll maxn=2e4+10;
 7 ll bit[maxn],n,num[maxn];
 8 struct node{
 9     ll x;
10     ll v;
11 }arr[maxn];
12 
13 bool cmp(node a,node b)
14 {
15     return a.v<b.v;
16 }
17 
18 ll lowbit(ll x)
19 {
20     return x&(-x);
21 }
22 
23 void add(ll* b,ll k,ll num)
24 {
25     while ( k<=n ) {
26         b[k]+=num;
27         k+=lowbit(k);
28     }
29 }
30 
31 ll sum(ll* b,ll k)
32 {
33     ll s=0;
34     while ( k ) {
35         s+=b[k];
36         k-=lowbit(k);
37     }
38     return s;
39 }
40 
41 int main()
42 {
43     ll m,i,j,k,x,y,z,N,s,cnt,u,v;
44     while ( scanf("%lld",&N)!=EOF ) {
45         n=0;
46         s=0;
47         memset(num,0,sizeof(num));
48         memset(bit,0,sizeof(bit));
49         for ( i=1;i<=N;i++ ) {
50             scanf("%lld%lld",&arr[i].v,&arr[i].x);
51             n=max(n,arr[i].x);
52         }
53         sort(arr+1,arr+N+1,cmp);
54         for ( i=1;i<=N;i++ ) {
55             x=sum(bit,arr[i].x); //坐标在它前面的所有坐标和 
56             y=sum(bit,n)-x;
57             v=sum(num,arr[i].x);
58             u=sum(num,n)-v;
59             s+=(arr[i].x*(v-u)-x+y)*arr[i].v;
60             add(bit,arr[i].x,arr[i].x);
61             add(num,arr[i].x,1);
62         }
63         printf("%lld\n",s);
64     }
65     return 0;
66 }
POJ1990

 

3.(POJ3321)http://poj.org/problem?id=3321

题意:有一颗苹果树,苹果树上有分叉,刚开始苹果树上每个节点都有一个苹果。现有两个操作:C X,如果X点有苹果,则拿掉,如果没有,则新长出一个;Q X,查询X点与它的所有后代分支一共有几个苹果。

分析:DFS序+树状数组(/线段树)。首先利用DFS序求出每个节点对应的区间范围。同时设置一个bool型的vis数组判断该位置上是否有苹果的存在。对于操作C来说,如果vis[x]为true,则利用树状数组在x位置上+1,否则-1.而对于操作Q,利用树状数组查询[in[x],out[x]]这个区间的苹果树总和。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<vector>
 5 using namespace std;
 6 const int maxn=1e5+10;
 7 int bit[maxn],n,cnt,in[maxn],out[maxn];
 8 vector<vector<int> >G(maxn);
 9 bool vis[maxn];
10 
11 int lowbit(int x)
12 {
13     return x&(-x);
14 }
15 
16 void add(int k,int num)
17 {
18     while ( k<=cnt ) {
19         bit[k]+=num;
20         k+=lowbit(k);
21     }
22 }
23 
24 int sum(int k)
25 {
26     int s=0;
27     while ( k ) {
28         s+=bit[k];
29         k-=lowbit(k);
30     }
31     return s;
32 }
33 
34 void dfs(int u)
35 {
36     in[u]=++cnt;
37     for ( int i=0;i<G[u].size();i++ ) {
38         int v=G[u][i];
39         dfs(v);
40     }
41     out[u]=cnt;
42 }
43 
44 int main()
45 {
46     int n,m,i,j,k,x,y,z,u,v;
47     char s[10];
48     while ( scanf("%d",&n)!=EOF ) {
49         memset(bit,0,sizeof(bit));
50         for ( i=1;i<=n;i++ ) {
51             G[i].clear();
52             vis[i]=true;
53         }
54         for ( i=1;i<n;i++ ) {
55             scanf("%d%d",&x,&y);
56             G[x].push_back(y);
57         }
58         cnt=0;
59         dfs(1);
60         for ( i=1;i<=n;i++ ) add(in[i],1);
61         scanf("%d",&m);
62         while ( m-- ) {
63             scanf("%s%d",s,&x);
64             if ( s[0]=='Q' ) {
65                 u=sum(out[x]);
66                 if ( in[x]!=1 ) u-=sum(in[x]-1);
67                 printf("%d\n",u);
68             }
69             else {
70                 if ( vis[x] ) {
71                     add(in[x],-1);
72                     vis[x]=false;
73                 }
74                 else {
75                     add(in[x],1);
76                     vis[x]=true;
77                 }
78             }
79         }
80     }
81     return 0;
82 }
POJ3321

 

4.(POJ1195)http://poj.org/problem?id=1195

题意:初始时给定一个大小为n*n的区域,现有4种操作。0为初始化该区域,该操作只会出现一次且在最初出现。1 x,y,z为在(x,y)上+z。 2 x1,y1,x2,y2为查询该区域内的数量。 3 为退出查询

分析:裸的二维树状数组,注意点同下一题

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<cmath>
 5 using namespace std;
 6 typedef long long ll;
 7 const int maxn=1500;
 8 ll bit[maxn][maxn],n;
 9 
10 ll lowbit(ll x)
11 {
12     return x&(-x);
13 }
14 
15 void add(ll x,ll y,ll num)
16 {
17     for ( ll i=x;i<=n;i+=lowbit(i) ) {
18         for ( ll j=y;j<=n;j+=lowbit(j) ) {
19             bit[i][j]+=num;
20         }
21     }
22 }
23 
24 ll sum(ll x,ll y)
25 {
26     ll s=0;
27     for ( ll i=x;i>0;i-=lowbit(i) ) {
28         for ( ll j=y;j>0;j-=lowbit(j) ) {
29             s+=bit[i][j];
30         }
31     }
32     return s;
33 }
34 
35 ll SUM(ll x1,ll y1,ll x2,ll y2)
36 {
37     return sum(x2,y2)-sum(x2,y1-1)-sum(x1-1,y2)+sum(x1-1,y1-1);
38 }
39 
40 int main()
41 {
42     ll m,i,j,k,x,y,z,x1,y1,x2,y2;
43     while ( scanf("%lld%lld",&m,&n)!=EOF ) {
44         memset(bit,0,sizeof(bit));
45         while ( scanf("%lld",&m) && m!=3 ) {
46             if ( m==1 ) {
47                 scanf("%lld%lld%lld",&x,&y,&z);
48                 x++;y++;
49                 add(x,y,z);
50             }
51             else if ( m==2 ) {
52                 scanf("%lld%lld%lld%lld",&x1,&y1,&x2,&y2);
53                 x1++;x2++;y1++;y2++;
54                 if ( x1>x2 ) swap(x1,x2);
55                 if ( y1>y2 ) swap(y1,y2);
56                 printf("%lld\n",SUM(x1,y1,x2,y2));
57             }
58         }
59     }
60     return 0;
61 }
POJ1195

 

5.(HDOJ2642)http://acm.hdu.edu.cn/showproblem.php?pid=2642

题意:有一个最大不超过1000*1000的二维平面,每个点有一颗星星,初始时都是暗的。现在有3种操作。D x y使(x,y)这个点上的星星暗掉。B(x,y)使(x,y)这个点上的星星亮起来。Q x1,x2,y1,y2。查询在这个区域内亮着的点的个数

分析:裸的二维树状数组。但是要注意两点,下标必须从1开始,而不是0(对所有输入的x和y采用+1的方式处理)。在查询时需要保证x1<=x2&&y1<=y2

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<cmath>
 5 using namespace std;
 6 const int maxn=1005;
 7 int bit[maxn][maxn],n;
 8 bool vis[maxn][maxn];
 9 
10 int lowbit(int x)
11 {
12     return x&(-x);
13 }
14 
15 void add(int x,int y,int num)
16 {
17     for ( int i=x;i<=n;i+=lowbit(i) ) {
18         for ( int j=y;j<=n;j+=lowbit(j) ) {
19             bit[i][j]+=num;
20         }
21     }
22 }
23 
24 int sum(int x,int y)
25 {
26     int s=0;
27     for ( int i=x;i>0;i-=lowbit(i) ) {
28         for ( int j=y;j>0;j-=lowbit(j) ) {
29             s+=bit[i][j];
30         }
31     }
32     return s;
33 }
34 
35 int SUM(int x1,int y1,int x2,int y2)
36 {
37     return sum(x2,y2)-sum(x2,y1-1)-sum(x1-1,y2)+sum(x1-1,y1-1);
38 }
39 
40 int main()
41 {
42     int m,i,j,k,x,y,z,x1,y1,x2,y2;
43     char s[10];
44     while ( scanf("%d",&m)!=EOF ) {
45         n=1000;
46         memset(bit,0,sizeof(bit));
47         memset(vis,false,sizeof(vis));
48         while ( m-- ) {
49             scanf("%s",s);
50             if ( s[0]=='B') {
51                 scanf("%d%d",&x,&y);
52                 x++;y++;
53                 if ( !vis[x][y] ) {
54                     add(x,y,1);
55                     vis[x][y]=true;
56                 }
57             }
58             else if ( s[0]=='D' ) {
59                 scanf("%d%d",&x,&y);
60                 x++;y++;
61                 if ( vis[x][y] )  {
62                     add(x,y,-1);
63                     vis[x][y]=false;
64                 }
65             }
66             else {
67                 scanf("%d%d%d%d",&x1,&x2,&y1,&y2);
68                 x1++;x2++;y1++;y2++;
69                 if ( x1>x2 ) swap(x1,x2);
70                 if ( y1>y2 ) swap(y1,y2);
71                 printf("%d\n",SUM(x1,y1,x2,y2));
72             }
73         }
74     }
75     return 0;
76 }
HDOJ2642

 

有关二进制在数据结构中的应用:https://wenku.baidu.com/view/1e51750abb68a98271fefaa8

6.(POJ2155)http://poj.org/problem?id=2155

题意:裸二维树状数组

分析:见以上那篇论文。采用二维树状数组进行保存,每次区间更新转化成点更新,每次更新都++,最后对2取模即可得到0/1。注意二维更新的点数是2^2个。n维更新的点数是2^n个。同时需要注意输出格式。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int maxn=1005;
 6 int bit[maxn][maxn],n;
 7 
 8 int lowbit(int x)
 9 {
10     return x&(-x);
11 }
12 
13 void add(int x,int y)
14 {
15     for ( int i=x;i<=n;i+=lowbit(i) ) {
16         for ( int j=y;j<=n;j+=lowbit(j) ) bit[i][j]++;
17     }
18 }
19 
20 int sum(int x,int y)
21 {
22     int s=0;
23     for ( int i=x;i>0;i-=lowbit(i) ) {
24         for ( int j=y;j>0;j-=lowbit(j) ) s+=bit[i][j];
25     }
26     return s;
27 }
28 
29 int main()
30 {
31     int T,m,i,j,k,x,y,z,x1,x2,y1,y2,q;
32     bool flag;
33     char s[10];
34     scanf("%d",&T);
35     flag=true;
36     while ( T-- ) {
37         scanf("%d%d",&n,&q);
38         memset(bit,0,sizeof(bit));
39         if ( flag ) flag=false;
40         else printf("\n");
41         while ( q-- ) {
42             scanf("%s",s);
43             if ( s[0]=='C' ) {
44                 scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
45                 add(x1,y1);
46                 add(x1,y2+1);
47                 add(x2+1,y1);
48                 add(x2+1,y2+1);
49             }
50             else {
51                 scanf("%d%d",&x,&y);
52                 printf("%d\n",sum(x,y)%2);
53             }
54         }
55     }
56     return 0;
57 }
POJ2155

7.(HDOJ3584)http://acm.hdu.edu.cn/showproblem.php?pid=3584

题意:裸三维树状数组

分析:见以上那篇论文。更新8个点即可。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int maxn=105;
 6 int bit[maxn][maxn][maxn],n;
 7 
 8 int lowbit(int x)
 9 {
10     return x&(-x);
11 }
12 
13 void add(int x,int y,int z)
14 {
15     for ( int i=x;i<=n;i+=lowbit(i) ) {
16         for ( int j=y;j<=n;j+=lowbit(j) ) 
17             for ( int k=z;k<=n;k+=lowbit(k) ) bit[i][j][k]++;
18     }
19 }
20 
21 int sum(int x,int y,int z)
22 {
23     int s=0;
24     for ( int i=x;i>0;i-=lowbit(i) ) {
25         for ( int j=y;j>0;j-=lowbit(j) ) 
26             for ( int k=z;k>0;k-=lowbit(k) ) s+=bit[i][j][k];
27     }
28     return s;
29 }
30 
31 int main()
32 {
33     int T,m,i,j,k,x,y,z,x1,x2,y1,y2,z1,z2,s,q;
34     while ( scanf("%d%d",&n,&q)!=EOF ) {
35         memset(bit,0,sizeof(bit));
36         while ( q-- ) {
37             scanf("%d",&s);
38             if ( s==1 ) {
39                 scanf("%d%d%d%d%d%d",&x1,&y1,&z1,&x2,&y2,&z2);
40                 add(x1,y1,z1);
41                 add(x2+1,y1,z1);
42                 add(x1,y2+1,z1);
43                 add(x1,y1,z2+1);
44                 add(x1,y2+1,z2+1);
45                 add(x2+1,y1,z2+1);
46                 add(x2+1,y2+1,z1);
47                 add(x2+1,y2+1,z2+1);
48             }
49             else {
50                 scanf("%d%d%d",&x,&y,&z);
51                 printf("%d\n",sum(x,y,z)%2);
52             }
53         }
54     }
55     return 0;
56 }
HDOJ3584

 

8.(POJ3067)http://poj.org/problem?id=3067

题意:日本有东海岸和西海岸,西海岸有n座城市,东海岸有m座城市,有q条高铁连接东西海岸,先求高铁之间的交点有多少

分析:先来看看什么时候会有交点,对于高铁路线1(x1,y1)(表示东海岸的x1与西海岸的y1相连,下同)和高铁路线2(x2,y2)当x1<x2&&y1>y2时有交点。那么我们可以考虑根据x对高铁路线进行从小到大的排序,当x相同时y考虑从小到大进行排序(因为x相同时不会有交点,所以只有y从小到大进行排序后才对结果不会产生影响)。采用逐一更新的方式,每次输入一条高铁线路时,利用树状数组求出有多少条线路的y是大于该线路的y,然后再更新该线路的y。

注意:高铁线路的范围比较大;最后的答案要用long long储存

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 typedef long long ll;
 6 const int maxn=1005;
 7 const int maxm=1e6+10;
 8 int bit[maxn],n,m;
 9 struct node{
10     int x;
11     int y;
12 }arr[maxm];
13 
14 int lowbit(int x)
15 {
16     return x&(-x);
17 }
18 
19 void add(int k,int num)
20 {
21     while ( k<=m ) {
22         bit[k]+=num;
23         k+=lowbit(k);
24     }
25 }
26 
27 int sum(int k)
28 {
29     int s=0;
30     while ( k ) {
31         s+=bit[k];
32         k-=lowbit(k);
33     }
34     return s;
35 }
36 
37 bool cmp(node a,node b)
38 {
39     if ( a.x==b.x ) return a.y<b.y;
40     return a.x<b.x;
41 }
42 
43 int main()
44 {
45     int T,h,i,j,k,x,y,z,q;
46     ll ans;
47     scanf("%d",&T);
48     for ( h=1;h<=T;h++ ) {
49         scanf("%d%d%d",&n,&m,&q);
50         memset(bit,0,sizeof(bit));
51         for ( i=0;i<q;i++ ) {
52             scanf("%d%d",&arr[i].x,&arr[i].y);
53         }
54         sort(arr,arr+q,cmp);
55         ans=0;
56         for ( i=0;i<q;i++ ) {
57             x=arr[i].x;
58             y=arr[i].y;
59             ans+=(i-sum(y));
60             add(y,1);
61         }
62         printf("Test case %d: %I64d\n",h,ans);
63     }
64     return 0;
65 }
POJ3067

 

9.(HDOJ3465)http://acm.hdu.edu.cn/showproblem.php?pid=3465

题意:有N条直线,先给定一个范围(L,R),每条直线给出两点坐标(x1,y1),(x2,y2),先求所有直线在开区间(L,R)中的交点有多少个

分析:求出每条直线与L和R的交点后通过离散化就可以转化为上面那题的形式了。此题有几个点需要注意,可能存在垂直于x轴的直线(即斜率k不存在)需要特别考虑。另外离散化的方法是先按ry从小到大排序然后按顺序给id(此处的id类似于上一题的y)赋值。离散化后的思路大致用上一题

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int maxn=5e4+10;
 6 struct node{
 7     double ly,ry;
 8     int id;
 9 }arr[maxn];
10 int n,bit[maxn];
11 
12 int lowbit(int x)
13 {
14     return x&(-x);
15 }
16 
17 void add(int k)
18 {
19     while ( k<=n ) {
20         bit[k]++;
21         k+=lowbit(k);
22     }
23 }
24 
25 int sum(int k)
26 {
27     int s=0;
28     while ( k ) {
29         s+=bit[k];
30         k-=lowbit(k);
31     }
32     return s;
33 }
34 
35 bool cmp1(node a,node b)
36 {
37     if ( a.ly==b.ly ) return b.ly<a.ly;
38     return a.ly<b.ly;
39 }
40 
41 bool cmp2(node a,node b)
42 {
43     if ( a.ry==b.ry ) return a.ly<b.ly;
44     return a.ry<b.ry;
45 }
46 
47 int main()
48 {
49     int N,i,j,now;
50     long long ans;
51     double L,R,x1,y1,x2,y2,k,b;
52     while ( scanf("%d",&N)!=EOF ) {
53         memset(bit,0,sizeof(bit));
54         scanf("%lf%lf",&L,&R);
55         n=0;
56         now=0;
57         for ( i=0;i<N;i++ ) {
58             scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
59             if ( x1==x2 ) {
60                 if ( L<x1 && x1<R ) now++;
61             }
62             else {
63                 k=(y2-y1)/(x2-x1);
64                 b=y1-x1*k;
65                 arr[n].ly=L*k+b;
66                 arr[n++].ry=R*k+b;
67             }
68         }
69         sort(arr,arr+n,cmp2);
70         for ( i=0;i<n;i++ ) arr[i].id=i+1;
71         ans=n*now;
72         sort(arr,arr+n,cmp1);
73         for ( i=0;i<n;i++ ) {
74             ans+=(i-sum(arr[i].id));
75             add(arr[i].id);
76         }
77         printf("%lld\n",ans);
78     }
79     return 0;
80 }
HDOJ3465

 

10.(POJ2481)http://poj.org/problem?id=2481

题意:有m头牛,每头牛都有一个吃草的区间[s,e],对于牛i和牛j,当牛j吃草区间是牛i的真子集时我们就说牛j比牛i强壮,先求对于每头牛有多少牛比它们强壮

分析:对牛按E从大到小,S从小到大进行排序进行离线操作。每次求sum(s[i]),所得的数量一定是比当前这头牛强壮的牛的数量。注意特判,当两头牛吃草的区间是一样时则不计算入内。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int maxn=1e5+10;
 6 int n,bit[maxn],num[maxn];
 7 struct node{
 8     int s;
 9     int e;
10     int index;
11 }arr[maxn];
12 
13 int lowbit(int x)
14 {
15     return x&(-x);
16 }
17 
18 void add(int k)
19 {
20     while ( k<=n ) {
21         bit[k]++;
22         k+=lowbit(k);
23     }
24 }
25 
26 int sum(int k)
27 {
28     int s=0;
29     while ( k ) {
30         s+=bit[k];
31         k-=lowbit(k);
32     }
33     return s;
34 }
35 
36 bool cmp(node a,node b)
37 {
38     if ( a.e==b.e ) return a.s<b.s;
39     return a.e>b.e;
40 }
41 
42 int main()
43 {
44     int m,i,j,k,x,y,z,cnt;
45     while ( scanf("%d",&m)!=EOF && m ) {
46         memset(bit,0,sizeof(bit));
47         memset(num,0,sizeof(num));
48         n=0;
49         for ( i=0;i<m;i++ ) {
50             scanf("%d%d",&arr[i].s,&arr[i].e);
51             arr[i].s++;arr[i].e++;
52             n=max(n,arr[i].e);
53             arr[i].index=i;
54         }
55         sort(arr,arr+m,cmp);
56         cnt=0;
57         for ( i=0;i<m;i++ ) {
58             if ( i!=0 ){
59                 if ( arr[i].s==arr[i-1].s && arr[i].e==arr[i-1].e ) cnt++;
60                 else cnt=0;
61             } 
62             num[arr[i].index]+=(sum(arr[i].s)-cnt);
63             add(arr[i].s);
64         }
65         for ( i=0;i<m;i++ ) {
66             printf("%d",num[i]);
67             if ( i!=m-1 ) printf(" ");
68             else printf("\n");
69         }
70     }
71     return 0;
72 }
POJ2481

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!