题目链接:http://poj.org/problem?id=1436
题目大意:有n条平行于y轴的线段,定义两线段可见,当且仅当这两条线段可以被一条水平线链接,且该水平线段不经过其他垂线段;定义三角形线段为两两可见的三条垂线段。现在给出n条线段,问你一共有多少组三角形线段。
分析:区间覆盖问题,POJ2777的升级版。两条线段a和b可见,就是说在存在某一区间[l,r],使得在此区间上b直接覆盖a。实现上,对于每一段区间,我们只纪录覆盖此区间的最后一条线段,设为color[rt],然后对于下一条覆盖此区间的线段id,我们就有了color[rt]和id是可见的,因为他们之间没有其他的线段了。
我们可以先把n条线段按x坐标排序,然后从右至左遍历每一条线段,对于第i条线段,我们先判断在i的左边,有没有和它可见的线段,如果有(设为线段j),标记vis(i,j)=true,意为线段i和j可见;然后把这条线段插入到线段树中,更新他的y坐标所覆盖区间的color的值,这样,我们在遍历完这n条线段之后,他们之间可见的关系就全部标记出来, 然后我们暴力找一下其中的三角形线段的组数就行了。
实现代码如下:
#include <cstdio> #include <algorithm> #include <cstring> using namespace std; const int M=8010; int color[M<<3]; //color[i]=j表示第i条线段最后被第j条线段覆盖 bool vis[M][M]; //vis[i][j]标记线段i和j是可见的 struct EDGE { int y1,y2,x,id; EDGE(){} EDGE(int a,int b,int c,int d):y1(a),y2(b),x(c),id(d){} bool operator <(const EDGE &a) const { return x<a.x; } }edge[M]; void pushdown(int rt) { if(color[rt]) { color[rt<<1]=color[rt<<1|1]=color[rt]; color[rt]=0; } } void update(int rt,int l,int r,int id,int il,int ir) { if(l>=il&&r<=ir) { color[rt]=id; return ; } pushdown(rt); int m=(l+r)>>1; if(il<=m) update(rt<<1,l,m,id,il,ir); if(ir>m) update(rt<<1|1,m+1,r,id,il,ir); } void query(int rt,int l,int r,int id,int il,int ir) { if(color[rt]) { vis[id][ color[rt] ]=true; return ; } if(l==r) return ; int m=(l+r)>>1; if(il<=m) query(rt<<1,l,m,id,il,ir); if(ir>m) query(rt<<1|1,m+1,r,id,il,ir); } int main() { int t,n; scanf("%d",&t); while(t--) { scanf("%d",&n); for(int i=1;i<=n;i++) { int y1,y2,x; scanf("%d%d%d",&y1,&y2,&x); edge[i]=EDGE(y1*2,y2*2,x,i); } sort(edge+1,edge+1+n); memset(color,0,sizeof(color)); memset(vis,false,sizeof(vis)); for(int i=1;i<=n;i++) { query(1,0,M*2,edge[i].id,edge[i].y1,edge[i].y2); update(1,0,M*2,edge[i].id,edge[i].y1,edge[i].y2); } int ans=0; for(int i=1;i<=n;i++) //暴力找三角形线段的组数 for(int j=1;j<=n;j++) if(vis[i][j]) for(int k=1;k<=n;k++) if(vis[i][k]&&vis[j][k]) ans++; printf("%d\n",ans); } return 0; }