【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\n",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;
}
【uoj57】 WC2013—平面图的更多相关文章
- [WC2013]平面图——平面图点定位
[WC2013]平面图 码农题 平面图点定位: 1.平面图转对偶图:[HNOI2016]矿区 2.扫描线点定位 把所有的顶点和询问点排序,扫描线 每个边在fr的位置加入,to的位置删除,竖直直线不要 ...
- bzoj3051: [wc2013]平面图
Description Input Output 扫描线求出平面图的对偶图然后求最小生成树,用并查集按秩合并,以便查询两点间路径最大权 #include<stdio.h> #include ...
- bzoj3051[WC2013]平面图(树上倍增+平面图转对偶图+扫描线)
简要题意:二维平面上n个点,点之间有一些连线,连线不在点之外的地方相交,将平面分为若干个区域.给出一些询问点对,问从这个点所在的区域走到另一个点所在的区域的最小代价. 题解:这道题首先可以把平面图转对 ...
- 洛谷 P4073 [WC2013]平面图
#include<bits/stdc++.h> using namespace std; ; typedef long double LD; ; ); int dcmp(LD x){ret ...
- bzoj AC倒序
Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...
- [BZOJ1997][HNOI2010] 平面图判定
Description Input Output 是的..BZOJ样例都没给. 题解(from 出题人): 如果只考虑简单的平面图判定,这个问题是非常不好做的. 但是题目中有一个条件— ...
- 【BZOJ 3051】【UOJ #57】【WC 2013】平面图
http://www.lydsy.com/JudgeOnline/problem.php?id=3051 http://uoj.ac/problem/57 这道题需要平面图转对偶图,点定位,最小生成树 ...
- 【BZOJ-2007】海拔 最小割 (平面图转对偶图 + 最短路)
2007: [Noi2010]海拔 Time Limit: 20 Sec Memory Limit: 552 MBSubmit: 2095 Solved: 1002[Submit][Status] ...
- 【BZOJ-4423】Bytehattan 并查集 + 平面图转对偶图
4423: [AMPPZ2013]Bytehattan Time Limit: 3 Sec Memory Limit: 128 MBSubmit: 144 Solved: 103[Submit][ ...
随机推荐
- lucene3.6.1 经典案例 入门教程 (包含从文件中读取content)
转载http://liqita.iteye.com/blog/1676664 第一步:下载lucene的核心包 lucene-core-3.6.1-javadoc.jar (3.5 MB) lucen ...
- PHP获取POST方式的XML数据
今天做微信支付开发,微信服务器回调的时候,会发送XML数据到我的服务器,用以往的POST,GET是获取不到的 百度了一下,应该是 $file_in = file_get_contents(" ...
- WiresShark 图解教程1
Wireshark是世界上最流行的网络分析工具.这个强大的工具可以捕捉网络中的数据,并为用户提供关于网络和上层协议的各种信息.与很多其他网络工具一样,Wireshark也使用pcap network ...
- Divisor Summation_
Divisor Summation Problem Description Give a natural number n (1 <= n <= 500000), please tell ...
- 超简单讲解JQ冒泡问题
当JQ代码越写越长的时候,冒泡的问题就会显得突出.所以对JQ冒泡问题的了解是十分必要的. 那么什么是冒泡了,个人简单的理解就是在触发子元素事件的时候父元素的事件也被动触发. 举个简单例子: 例如有一个 ...
- 数据库建表的时候报 “1215 Cannot add foreign key constraint”
很大原因是因为: 引用表中的字段类型和被引用的主键的类型不统一. 比如说学生表中有一个班级ID字段引用班级表. 班级表的ID是int类型,学生表中的班级ID是Varchar类型. 肯定会提示上述121 ...
- 基于Spring的异步系统实现方案
一般的实现方案 发送异步消息所使用的工具类: import java.util.Date; import javax.jms.Destination; import javax.jms.JMSExce ...
- PS2鼠标+LCD12864实验(调试未成功)
此试验我一人调试许久都未成功,但发送ff时,读出来的数据确是对的,一开始让我窃喜,但发送f4时,读出来的数据确是错的,哎让苦恼啊,能力有限,只能先暂时就这样吧,那位什么还要贴出来呢,有两个原因: 1. ...
- System.InvalidCastException: 无法将类型为“Microsoft.Office.Interop.Word.ApplicationClass”的 COM 对象强制转换为接口类型“Microsoft.Office.Interop.Word._Application”。
报错:System.InvalidCastException: 无法将类型为“Microsoft.Office.Interop.Word.ApplicationClass”的 COM 对象强制转换为接 ...
- PAT (Advanced Level) 1017. Queueing at Bank (25)
简单模拟. #include<iostream> #include<cstring> #include<cmath> #include<algorithm&g ...