pku 1436 Horizontally Visible Segments
题意:在水平坐标系内,有n条垂直线段,问任意三条线段组成一组,问有多少组线段满足,其中任意俩条线段水平可见。
水平可见满足:存在一条水平线段将俩条线段连接起来,并且中间不与其他任意线段有交点
分析:这是一个染色的问题。首先将线段按横坐标进行排序,接着,从左往右判断当前线段与左边的线段是否水平可见。问题转换:将线段的编号理解为该线段的颜色,那么判断该线段与前面已经插入的线段是否水平可见,只需在查询过程中判断与该区间相交的区间是否存在整个被染成某一种颜色,如果是,则该区间的颜色标号代表的线段与当前线段水平可见。这里要注意查询与更新的顺序。
还有,直接按输入坐标做,会发现,线段是整数性质的,对于单位长度的(2,3)这种小数的无法处理.解决办法学到了,坐标乘2就可以了,原因是,任意的单位长度的线段[x,x+1],乘2变成[2x,2x+2],去掉端点,(2x,2x+2),中间多了整数性质的段,该段用来表示之前无法处理的小数部分.
接下来就是一个暴力统计。
具体看代码吧:
#include<iostream> #include<vector> #include<algorithm> #define MAXN 8000+5 using namespace std; int p[MAXN<<3],hash1[MAXN]; vector<int> edge[MAXN]; //保存水平可见的线段对 struct seg { int y,y1,x; }se[MAXN]; bool cmp(seg a,seg b) { return a.x<b.x; } void update(int k,int x,int s,int t,int l,int r) { if(l<=s && t<=r) { p[k]=x; return ; } int kl=k<<1,kr=kl+1,mid=(s+t)>>1; if(p[k]!=-1)//将区间被覆盖的颜色传给左右儿子 { p[kl]=p[kr]=p[k]; p[k]=-1; } if(l<=mid) update(kl,x,s,mid,l,r); if(r>mid) update(kr,x,mid+1,t,l,r); } void query(int k,int x,int s,int t,int l,int r) { if(p[k]!=-1) { if(hash1[p[k]]!=x) { hash1[p[k]]=x;//hash1[]数组保存当前区间被覆盖的颜色是否等于要插入的颜色,或者说,避免颜色重复插入x的可见线段中 edge[x].push_back(p[k]); } return ; } if(s==t) return ; int kl=k<<1,kr=kl+1,mid=(s+t)>>1; if(l<=mid) query(kl,x,s,mid,l,r); if(r>mid) query(kr,x,mid+1,t,l,r); } int main() { int cas,n,a,b; scanf("%d",&cas); while(cas--) { scanf("%d",&n); for(int i=0;i<n;i++) { scanf("%d %d %d",&a,&b,&se[i].x); se[i].y=a*2; se[i].y1=b*2; edge[i].clear(); } sort(se,se+n,cmp); memset(p,-1,sizeof(p)); memset(hash1,-1,sizeof(hash1)); for(int i=0;i<n;i++) { query(1,i,0,8000*2,se[i].y,se[i].y1); update(1,i,0,8000*2,se[i].y,se[i].y1); } int ans=0; for(int i=0;i<n;i++)//暴力统计 { int sz=edge[i].size();//与i可见的线段总数 for(int j=0;j<sz;j++) { int ss=edge[i][j];//ss与i可见 int m=edge[ss].size(); for(int k=0;k<sz;k++) for(int l=0;l<m;l++) if(edge[i][k]==edge[ss][l]) ans++; } } printf("%d\n",ans); } }