专题训练之树状数组
推荐几个博客: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大数
一维树状数组模板(区间求和、单点修改)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
二维树状数组模板(区间求和、单点修改)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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数的数量之和。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
注意两点: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坐标的牛的数量)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
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]]这个区间的苹果树总和。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
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 为退出查询
分析:裸的二维树状数组,注意点同下一题
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
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
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
有关二进制在数据结构中的应用:https://wenku.baidu.com/view/1e51750abb68a98271fefaa8
6.(POJ2155)http://poj.org/problem?id=2155
题意:裸二维树状数组
分析:见以上那篇论文。采用二维树状数组进行保存,每次区间更新转化成点更新,每次更新都++,最后对2取模即可得到0/1。注意二维更新的点数是2^2个。n维更新的点数是2^n个。同时需要注意输出格式。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
7.(HDOJ3584)http://acm.hdu.edu.cn/showproblem.php?pid=3584
题意:裸三维树状数组
分析:见以上那篇论文。更新8个点即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
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储存
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
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)赋值。离散化后的思路大致用上一题
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
10.(POJ2481)http://poj.org/problem?id=2481
题意:有m头牛,每头牛都有一个吃草的区间[s,e],对于牛i和牛j,当牛j吃草区间是牛i的真子集时我们就说牛j比牛i强壮,先求对于每头牛有多少牛比它们强壮
分析:对牛按E从大到小,S从小到大进行排序进行离线操作。每次求sum(s[i]),所得的数量一定是比当前这头牛强壮的牛的数量。注意特判,当两头牛吃草的区间是一样时则不计算入内。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }