POJ-2481 Cows (线段树单点更新)

时间:2021-04-17 12:41:20

题目大意:给n个区间,对于任意一个区间,求出能完全包含它并且长度比它长的区间的个数。

题目分析:将一个区间视为二位坐标系中的一个点,左端点视作横坐标,右端点视作纵坐标。则题目变成了求每个点的左上方、正左方、正上方点的个数。将所有坐标先按纵坐标从大到小、再按横坐标从小到大排序。按顺序便利所有坐标,满足横坐标比当前坐标小或者满足横坐标不比当前横坐标大并且纵坐标比当前纵坐标大的点都是要找的点。用线段树维护即可,也可以用树状数组。

代码如下:

# include<iostream>
# include<cstdio>
# include<map>
# include<cstring>
# include<algorithm>
using namespace std;
# define LL long long const int N=100000;
const int INF=10000000; struct Coord
{
int x,y,id;
bool operator < (const Coord& a) const{
if(y==a.y) return x<a.x;
return y>a.y;
}
};
Coord c[N+5];
int ans[N+5];
int tr[N*4+5];
int vis[N+5]; void update(int rt,int l,int r,int x)
{
if(l==r){
++tr[rt];
}else{
int mid=l+(r-l)/2;
if(x<=mid)
update(rt<<1,l,mid,x);
else
update(rt<<1|1,mid+1,r,x);
tr[rt]=tr[rt<<1]+tr[rt<<1|1];
}
} int query(int rt,int l,int r,int L,int R)
{
if(L>R) return 0;
if(L<=l&&r<=R)
return tr[rt];
int res=0;
int mid=l+(r-l)/2;
if(L<=mid)
res+=query(rt<<1,l,mid,L,R);
if(R>mid)
res+=query(rt<<1|1,mid+1,r,L,R);
return res;
} int main()
{
int n;
while(~scanf("%d",&n)&&n)
{
memset(tr,0,sizeof(tr));
memset(ans,0,sizeof(ans));
memset(vis,0,sizeof(vis));
for(int i=0;i<n;++i){
scanf("%d%d",&c[i].x,&c[i].y);
c[i].id=i;
}
sort(c,c+n);
vis[0]=0;
for(int i=1;i<n;++i){
if(c[i].x==c[i-1].x&&c[i].y==c[i-1].y) vis[i]=vis[i-1]+1;
else vis[i]=0;
}
for(int i=0;i<n;++i){
ans[c[i].id]+=query(1,0,N,0,c[i].x)-vis[i];
update(1,0,N,c[i].x);
}
for(int i=0;i<n;++i)
printf("%d%c",ans[i],(i==n-1)?'\n':' ');
}
return 0;
}