【解题报告】pojP1436 Horizontally Visible Segments
http://poj.org/problem?id=1436
题目大意:有n条平行于x轴的线段,每条线段有y坐标,如果两条线段有一段x坐标数值相等,且中间没有其它线段阻隔,则称这两条线段”照面“。如果三条线段两两互能照面,则称这三条线段为一组。问这n条线段中有多少组?
可以看到题目中n<=8000,于是开始想n log n的算法,但是当我看那题的discuss时,有人说
这题数据太无语了……O(n^2lgn) TLE , O(n^3)的算法266ms……
O(n^3)能过?于是想到如果能判断并保存两两线段之间的是否照面关系,然后n*n*n暴力搜索互相照面的三条线段。。。
现在问题只剩下如何判断并保存两两线段之间的是否照面关系了,这就是典型的线段树区间覆盖问题
1、先把所有线段按x坐标排一下序
2、线段树a[i].l表示左边界,a[i].r表示右边界,a[i].n表示占据该区域的线段号码,建树
3、压过程:把线段从树顶压下去,若碰到延迟标记就顺便压下子树,若碰到a[i].n!=0的子树,mark[a[i].n][x(目前压的线段号)]=1
4、冲过程:把线段加入线段树,找到属于该线段的区间(顺路推下延迟标记),若发现该区间a[i].n!=0,直接覆盖掉!因为线段已经被排过序,所以从宏观上看,就是x坐标大的线段把x坐标小的线段挡住了,以后的线段也不会再在该区间与x坐标小的线段照面了(想的时候在这里卡了很长时间)
5、回到第三步,直到所有线段都经过了冲压过程
6、O(n^3)暴力搜索互相照面的三条线段
我的程序:
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; bool mark[8003][8003]; int n; struct { int l,r,n; } a[8003*5]; struct node { int x,y1,y2; } s[8003]; int cmp(node a,node b) { return a.x<b.x; } void build(int l,int r,int i) { a[i].l=l; a[i].r=r; a[i].n=0; if(l==r) return; int k=(l+r)/2; build(l,k,2*i); build(k+1,r,2*i+1); } void add(int l,int r,int i,int m) { if ((l<=a[i].l)&&(a[i].r<=r)) { a[i].n=m; return; } if (a[i].n!=-1) { a[2*i].n=a[2*i+1].n=a[i].n; a[i].n=-1; } if (l<=a[2*i].r) add(l,r,2*i,m); if (r>=a[2*i+1].l) add(l,r,2*i+1,m); } void push(int l,int r,int i,int m) { if (a[i].n!=-1) { mark[a[i].n][m]=1; return; } if ((a[i].l)==(a[i].r)) return; if (a[i].n!=-1) { a[2*i].n=a[2*i+1].n=a[i].n; a[i].n=-1; } if (l<=a[2*i].r) push(l,r,2*i,m); if (r>=a[2*i+1].l) push(l,r,2*i+1,m); } void show() { int i,j,k; for(i=1;i<=n;i++) { for(j=1;j<=n;j++) printf ("%d ",mark[i][j]); printf ("\n"); } } int main() { int t,ans,i,x,y1,y2,j,k; scanf("%d",&t); while(t--) { scanf("%d",&n); for(i=1;i<=n;i++) { scanf("%d%d%d",&s[i].y1,&s[i].y2,&s[i].x); s[i].y1*=2; s[i].y2*=2; } sort(s+1,s+1+n,cmp); memset(mark,false,sizeof(mark)); build(0,16000,1); for(i = 1; i<=n; i++) { push(s[i].y1,s[i].y2,1,i); add(s[i].y1,s[i].y2,1,i); show(); printf ("\n"); } ans=0; for(i=1;i<=n;i++) for(j=1;j<=n;j++) if (mark[i][j]) for (k=1;k<=n;k++) if ((mark[i][k])&&(mark[j][k])) ans++; printf("%d\n",ans); } }

浙公网安备 33010602011771号