0SGU 128 snake (&& ZOJ 3521) 尺取,排序二叉树,线段树 难度:2
128. Snake
time limit per test: 0.25 sec.
memory limit per test: 4096 KB
There are N points given by their coordinates on a plane. All coordinates (xi,yi) are integers in a range from -10000 up to 10000 inclusive . It is necessary to construct a broken line satisfying the following conditions:
1. The broken line should be closed.
2. End points of each segment (verteces) of the broken line can only be the given points, and all given points should be used.
3. Each two consecutive segments of the broken line should form a corner of 90 degrees in each vertex point.
4. The sides of the broken line should be parallel to coordinate axes.
5. The broken line should have no self-crossing and self-contact.
6. The broken line should have the minimal length.
You have to either find the length L of the constructed broken line, or determine that it is impossible to construct such a broken line.
Input
First line contains the number N (4 <= N <= 10000) - amount of points. Each of the following N lines contains coordinates of points separated by space xi and yi (1 <= i <= N). Points are given in random order.
Output
First line should contain the length of the broken line L or 0 if there is no solution.
Sample Input
Sample Output
4 0 0 0 3 3 3 3 0
Sample Output
12
看了题解,发现这个不自交是每个点只有两条边的意思,所以必然从左下角出发只有一个解,注意到这一点之后胡乱搞搞就过了,,,,
大概就是,首先确定整个图是由平行于x,y轴线段组成的,每个点的度都是2的图
然后,对每个x段,在起点取y值,在终点去掉y值,对应在线段树上就是起点时+1,终点时-1,x段互相之间必然不相交
所以统计是否相交只需要看y段和x段是否交,对所有的y段看是否有(y1,y2)的开区间内的点y0已经存在线段树上了(也就是有一条高度为y1<y0<y2的线段,这个时候线段还在树上所以x1<=x
<=x2)
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define lx (x<<1) #define rx ((x<<1)|1) #define MID ((l+r)>>1) const int maxn=10001; int n; int dat[maxn<<4]; struct pnt{ int x,y,ind; }p[maxn]; int e[maxn][3],len[maxn]; bool vis[maxn*2]; void dfs(int s){ vis[s]=true; for(int i=0;i<2;i++){ int to=e[s][i]; if(!vis[to])dfs(to); } } bool cmpx(pnt p1,pnt p2){ if(p1.x!=p2.x)return p1.x<p2.x; return p1.y<p2.y; } bool cmpy(pnt p1,pnt p2){ if(p1.y!=p2.y)return p1.y<p2.y; return p1.x<p2.x; } void update(int pos,int del ,int l,int r,int x){ if(l==r){dat[x]+=del;return ;} if(pos<=MID)update(pos,del,l,MID,lx); else update(pos,del,MID+1,r,rx); dat[x]=dat[lx]+dat[rx]; } int query(int L,int R,int l,int r,int x){ if(L<=l&&r<=R)return dat[x]; int ans=0; if(L<=MID)ans+=query(L,R,l,MID,lx); if(MID<R)ans+=query(L,R,MID+1,r,rx); return ans; } void addedge(int f,int t){ if(len[f]<3)e[f][len[f]++]=t; if(len[t]<3)e[t][len[t]++]=f; } int main(){ scanf("%d",&n); int maxy=0; for(int i=0;i<n;i++){ scanf("%d%d",&p[i].x,&p[i].y); p[i].x+=maxn; p[i].y+=maxn; maxy=max(maxy,p[i].y); } sort(p,p+n,cmpx); for(int i=0;i<n;i++){ p[i].ind=i; } int ans=0; sort(p,p+n,cmpy); for(int i=1;i<n;i+=2){ if(p[i].y==p[i-1].y){addedge(p[i-1].ind,p[i].ind);ans+=p[i].x-p[i-1].x;} } sort(p,p+n,cmpx); for(int i=1;i<n;i+=2){ if(p[i].x==p[i-1].x){addedge(p[i-1].ind,p[i].ind);ans+=p[i].y-p[i-1].y;} } for(int i=0;i<n;i++){ if(len[i]!=2){puts("0");return 0;} } dfs(0); for(int i=0;i<n;i++){ if(!vis[i]){puts("0");return 0;} } memset(vis,0,sizeof(vis)); for(int i=0;i<n;i++){ int x1=p[i].x,x2=p[e[i][0]].x; int y1=p[i].y,y2=p[e[i][1]].y; if(vis[i])update(y1,-1,1,maxy,1); int k1=y2>2?query(1,y2-1,1,maxy,1):0;//因为是重叠着做的而不是分开来更新所有x再检验的,也就是说这个x对应的y需要被取下但是还没取,所以通过事件顺序没法规范发生,需要注意去掉端点问题 int k2= query(1,y1,1,maxy,1); if(y2-y1>1 && (k1-k2)>0){puts("0");return 0;} if(!vis[i])update(y1,1,1,maxy,1); vis[i] = !vis[i]; vis[e[i][0]] = !vis[e[i][0]]; } printf("%d\n",ans); return 0; }