zoukankan      html  css  js  c++  java
  • 【uoj57】 WC2013—平面图

    http://uoj.ac/problem/57 (题目链接)

    题意

      给出二位平面上n个点,点之间有一些连线,连线不在顶点之外的地方相交,将平面分为若干个区域。给出一些询问点对,问从这个点所在的区域走到另一个点所在的区域的最小代价。

    Solution

      最小生成树&&树上倍增+平面图转对偶图+点定位

      前两个就不说了,网上题解很多,也很显然,真正恶心的是点定位,细节多且难写。

      我们只看从左指向右的线段。首先运用扫描线的思想,扫到左端点加入平衡树,扫到右端点从平衡树中删除。考虑怎么比较平衡树中两条线段的位置关系。

      因为两条直线互不相交,所以它们的相对位置不会发生改变。于是乎我们建立起一个直角坐标系,其中y轴可以随意左右平移。对于每一条线段,我们使它的右端点正好在y轴上,然后选择两线段左端点x坐标比较大的作为比较直线,计算这条直线与两线段的交点的高低。

      考虑如何处理查询(x,y)。我们新建一个线段从(x,y)指向(x-1,y),也就是说这条线段是从右指向左的,为什么这么做呢,因为我们要避免计算(x-1,y),因为它可能会与某条线段相交。

      那能不能将线段的左端点与y轴对齐呢?答案是不行的,我也不清楚为什么,反正只有80分→_→,如果有人AC了,跪求指教。。。

    细节

      码农题,最好用namespace

    代码

    // uoj57
    #include<algorithm>
    #include<iostream>
    #include<cstdlib>
    #include<cstring>
    #include<vector>
    #include<cstdio>
    #include<cmath>
    #include<set>
    #define LL long long
    #define inf 1<<30
    #define eps 1e-8
    #define Pi acos(-1.0)
    #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
    using namespace std;
    
    const int maxn=100010;
    int N,q,n,m,belong[maxn<<1];
    
    struct edge {int to,next,w,id;}e[maxn<<1];   //邻接表
    struct Edge {int u,v,w;double o;}d[maxn<<1];   //有向线段
    struct event {   //事件:0删除,1插入,2查询
    	int x,y,t;
    	friend bool operator < (const event a,const event b) {
    		return a.x!=b.x ? a.x<b.x : a.t<b.t;
    	}
    };
    struct point {   //点
    	int x,y,id;
    	void Init(int i) {
    		double xx,yy;
    		scanf("%lf%lf",&xx,&yy);
    		x=(int)(xx*2+eps);y=(int)(yy*2+eps);id=i;
    	}
    }p[maxn],Q[maxn<<1];
    
    namespace MST {   //最小生成树+树上倍增
    	Edge E[maxn<<1];
    	edge ee[maxn<<1];
    	int cnt,cc,f[maxn],head[maxn];
    	int fa[maxn][30],d[maxn][30],deep[maxn],bin[30];
    	
    	void link(int u,int v,int w) {
    		ee[++cc]=(edge){v,head[u],w};head[u]=cc;
    		ee[++cc]=(edge){u,head[v],w};head[v]=cc;
    	}
    	int find(int x) {
    		return f[x]==x ? x : f[x]=find(f[x]);
    	}
    	bool cmp(Edge a,Edge b) {
    		return a.w<b.w;
    	}
    	void add(int u,int v,int w) {
    		E[++cnt]=(Edge){u,v,w};
    	}
    	void dfs(int x) {
    		for (int i=1;i<=20;i++) {
    			fa[x][i]=fa[fa[x][i-1]][i-1];
    			d[x][i]=max(d[x][i-1],d[fa[x][i-1]][i-1]);
    		}
    		for (int i=head[x];i;i=ee[i].next) if (ee[i].to!=fa[x][0]) {
    				deep[ee[i].to]=deep[x]+1;
    				fa[ee[i].to][0]=x;
    				d[ee[i].to][0]=ee[i].w;
    				dfs(ee[i].to);
    			}
    	}
    	int lca(int x,int y) {
    		if (deep[x]<deep[y]) swap(x,y);
    		int res=0,t=deep[x]-deep[y];
    		for (int i=0;bin[i]<=t;i++) if (bin[i]&t) res=max(res,d[x][i]),x=fa[x][i];
    		for (int i=20;i>=0;i--)
    			if (fa[x][i]!=fa[y][i]) res=max(res,max(d[x][i],d[y][i])),x=fa[x][i],y=fa[y][i];
    		return x==y ? res : max(res,max(d[y][0],d[x][0]));
    	}
    	void Init() {
    		bin[0]=1;for (int i=1;i<=20;i++) bin[i]=bin[i-1]<<1;
    		sort(E+1,E+cnt+1,cmp);
    		for (int i=1;i<=N;i++) f[i]=i;
    		for (int i=1;i<=cnt;i++) {
    			int r1=find(E[i].u),r2=find(E[i].v);
    			if (r1!=r2) {
    				link(E[i].u,E[i].v,E[i].w);
    				f[r1]=r2;
    			}
    		}
    		dfs(1);
    		for (int i=1;i<=q;i++) {
    			if (belong[i]==1 || belong[i+q]==1) {puts("-1");continue;}
    			int ans=lca(belong[i],belong[i+q]);
    			if (ans==inf) puts("-1");
    			else printf("%d
    ",ans);
    		}
    	}
    }
    
    namespace ScanLine {   //扫描线
    	struct cmp {   //判断相对位置的高低,从高往低排序
    		bool operator() (int A,int B) {
    			if (d[A].u==d[B].u) return d[A].o>d[B].o;
    			int x=max(p[d[A].u].x,p[d[B].u].x);   //只能y轴对齐右端点
    			double yA=1.0*(p[d[A].v].y-p[d[A].u].y)*(x-p[d[A].v].x)/(p[d[A].v].x-p[d[A].u].x)+p[d[A].v].y;
    			double yB=1.0*(p[d[B].v].y-p[d[B].u].y)*(x-p[d[B].v].x)/(p[d[B].v].x-p[d[B].u].x)+p[d[B].v].y;
    			return yA>yB;
    		}
    	};
    	set<int,cmp> T;   //平衡树
    	event ev[maxn<<3];   //事件
    	int cnt=0;
    
    	void Init() {
    		for (int i=2;i<=(m<<1)+1;i++)   //加入指向右方的线段
    			if (p[d[i].u].x<p[d[i].v].x) 
    				ev[++cnt]=(event){p[d[i].u].x,i,1},ev[++cnt]=(event){p[d[i].v].x,i,0};
    		for (int i=1;i<=q;i++) {   //加入询问点
    			ev[++cnt]=(event){Q[i].x,i,2};
    			ev[++cnt]=(event){Q[i+q].x,i+q,2};
    		}
    		sort(ev+1,ev+1+cnt);   //按左端点排序,若左端点相同则先删除后插入再查询
    		for (int i=1;i<=cnt;i++) {
    			if (ev[i].t==0) T.erase(ev[i].y);   //删除
    			else if (ev[i].t==1) T.insert(ev[i].y);   //插入
    			else {   //查询
    				p[n+1]=(point){ev[i].x,Q[ev[i].y].y,0};
    				p[n+2]=(point){ev[i].x-1,Q[ev[i].y].y,0};
    				d[(m<<1)+2]=(Edge){n+1,n+2,0,atan2(p[n+2].y-p[n+1].y,p[n+2].x-p[n+1].x)};   //将询问点构造为长度为1的新线段
    				//注意到这条线段的方向是从右指向左的,因为我们要算的是p[n+1]的相对位置而不是p[n+2]的相对位置(因为p[n+2]可能与别的点或线段重合)
    				T.insert((m<<1)+2);   //将新线段插入平衡树
    				set<int,cmp>::iterator j=T.find((m<<1)+2);   //查询其在平衡树中的位置
    				if (j!=T.begin()) {   //其上方的线段为e[*(j-1)]
    					j--;
    					belong[ev[i].y]=e[*j].id;   //记录它所在的域
    				}
    				else belong[ev[i].y]=1;   //上方不存在线段,属于无穷域
    				T.erase((m<<1)+2);
    			}
    		}
    	}
    }
    
    namespace Graph {   //平面图转对偶图
    	int nxt[maxn<<1],head[maxn],cnt=1;
    	vector<pair<double,int> > V[maxn];
    	point s=(point){inf,inf,0};
    	
    	void link(int u,int v,int w) {
    		e[++cnt]=(edge){v,head[u],w,-1};head[u]=cnt;
    		e[++cnt]=(edge){u,head[v],w,-1};head[v]=cnt;
    	}
    	void sorted(int x) {   //对节点x的出边极角排序
    		for (int i=head[x];i;i=e[i].next)
    			V[x].push_back(make_pair(atan2(p[e[i].to].y-p[x].y,p[e[i].to].x-p[x].x),i));
    		sort(V[x].begin(),V[x].end());
    		for (int i=0,j=V[x].size();i<j;i++) nxt[V[x][i].second^1]=V[x][(i+1)%j].second;
    	}
    	void find(int x) {
    		for (int i=x;e[i].id<0;i=nxt[i])
    			e[i].id=N;
    	}
    	void Init() {
    		cnt=1;
    		for (int i=1;i<=n;i++) {   //读入顶点
    			p[i].Init(i);
    			if (s.x>p[i].x || (s.x==p[i].x && s.y>p[i].y)) s=p[i];
    		}
    		for (int u,v,w,i=1;i<=m;i++) {   //读入顶点之间的边
    			scanf("%d%d%d",&u,&v,&w);
    			d[i<<1]=(Edge){u,v,w,atan2(p[v].y-p[u].y,p[v].x-p[u].x)};
    			d[(i<<1)+1]=(Edge){v,u,w,atan2(p[u].y-p[v].y,p[u].x-p[v].x)};
    			link(u,v,w);
    		}
    		scanf("%d",&q);
    		for (int i=1;i<=q;i++) Q[i].Init(i),Q[i+q].Init(i+q);   //读入询问点
    		for (int i=1;i<=n;i++) sorted(i);
    		N=1;   //无穷域编号为1
    		find(V[s.id][0].second);   //先找无穷域
    		for (int i=1;i<=cnt;i++) if (e[i].id<0) {N++;find(i);}
    		for (int i=2;i<=cnt;i+=2) {   //生成对偶图的边
    			if (e[i].id!=1 && e[i^1].id!=1) MST::add(e[i].id,e[i^1].id,e[i].w);
    			else MST::add(e[i].id,e[i^1].id,inf);
    		}
    	}
    }
    
    int main() {
    	scanf("%d%d",&n,&m);
    	Graph::Init();
    	ScanLine::Init();
    	MST::Init();
        return 0;
    }
    
  • 相关阅读:
    直击视频会议行业五大痛点提出企业视频会议通话完美解决方案
    网页直播/点播播放器支持httpflv/rtmp/m3u8等播放音视频流媒体播放器EasyPlayerRTMPiOS播放视频宽高变化导致播放器停止运行的问题解决
    流媒体音视频服务云管理平台EasyNVS平台中视频播放页面出现错误码的问题解决
    高稳定、低延时、高并发RTMP播放器流媒体音视频播放器EasyPlayerRTMPiOS器如何将核心代码打包成静态库
    网页直播/点播播放器支持httpflv/rtmp/m3u8等播放流媒体音视频播放器EasyPlayerRTMPiOS使用YUV渲染画面的方法
    网页全终端安防视频流媒体播放器EasyPlayer.js如何实现在web浏览器播放H.265编码视频?
    网页直播/点播播放器支持httpflv/rtmp/m3u8等播放音视频流媒体播放器EasyPlayerRTMPiOS卡顿问题的解决及设置方法
    HLS播放器RTSP播放器支持8K播放且低延时高并发全功能流媒体播放器EasyPlayer搭建之HTML中 px,em,rem该如何区别?
    移动端与PC端无缝衔接视频会议系统EasyRTC视频会议通话系统之会议录像检索回看功能解析
    企业远程会议系统EasyRTC视频会议通话软件如何保证会议的通话质量、噪音消除、信息高效共享及系统集成
  • 原文地址:https://www.cnblogs.com/MashiroSky/p/6272822.html
Copyright © 2011-2022 走看看