Hubtown

时间限制: 10 Sec  内存限制: 256 MB

题目描述

Hubtown is a large Nordic city which is home to n citizens. Every morning, each of its citizens wants to travel to the central hub from which the city gets its name, by using one of the m commuter trains which pass through the city. Each train line is a ray (i.e., a line segment which extends infinitely long in one direction), ending at the central hub, which is located at coordinates (0, 0). However, the train lines have limited capacity (which may vary between train lines), so some train lines may become full, leading to citizens taking their cars instead of commuting. The city council wishes to minimize the number of people who go by car. In order to do this, they will issue instructions stating which citizens are allowed to take which train. 
A citizen will always take the train line which is of least angular distance from its house. However, if a citizen is exactly in the middle between two train lines, they are willing to take either of them, and city council can decide which of the two train lines the citizen should use. 
See Figure H.1 for an example.

Figure H.1: Illustration of Sample Input 1. The dashed arrows indicate which train lines the citizens are closest to (note that we are measuring angular distances, not Euclidean distance).
Your task is to help the council, by finding a maximum size subset of citizens who can go by train in the morning to the central hub, ensuring that each of the citizens take one of the lines they are closest to, while not exceeding the capacity of any train line. For this subset, you should also print what train they are to take.

输入

The first line of input contains two integers n and m, where 0 ≤ n ≤ 200 000 is the number of citizens, and 1 ≤ m ≤ 200 000 is the number of train lines.
The next n lines each contain two integers x and y, the Cartesian coordinates of a citizen’s home. No citizen lives at the central hub of the city.
Then follow m lines, each containing three integers x, y, and c describing a train line, where (x, y) are the coordinates of a single point (distinct from the central hub of the city) which the train line passes through and 0 ≤ c ≤ n is the capacity of the train line. The train line is the ray starting at (0, 0) and passing through (x, y).
All coordinates x and y (both citizens’ homes and the points defining the train lines) are bounded by 1000 in absolute value. No two train lines overlap, but multiple citizens may live at the same coordinates.

输出

First, output a single integer s – the maximum number of citizens who can go by train. Then,output s lines, one for each citizen that goes by train. On each line, output the index of the citizen followed by the index of the train line the citizen takes. The indices should be zero-indexed (i.e.,between 0 and n − 1 for citizens, and between 0 and m − 1 for train lines, respectively), using the same order as they were given in the input.

样例输入

3 2
2 0
-1 0
-2 -1
1 -1 1
1 1 2

样例输出

3
0 1
1 1
2 0

 

题意:n个人,m个铁轨,每个人要到最近的铁轨去,若最近的有两个可二选一,每个铁轨能承受的人数有限,问最多多少个人可以到铁轨上。

做法:先对人和铁轨一起进行极角排序,然后记录一下距离人最近的上下两个铁轨,之后建图跑最大流。具体细节在代码中说明。

此外,这题能跑最大流是因为网络流跑二分图匹配的时间复杂度是 O(m*sqrt(n)),而且实际编程中速度会更快。

#include<bits/stdc++.h>
#define N 400050
#define M 2000050
using namespace std;
typedef struct
{
int v;
int flow;
} ss; ss edg[M];
vector<int>edges[N];
int now_edges=; void addedge(int u,int v,int flow)
{
// printf(" %d %d %d\n",u,v,flow);
edges[u].push_back(now_edges);
edg[now_edges++]=(ss)
{
v,flow
};
edges[v].push_back(now_edges);
edg[now_edges++]=(ss)
{
u,
};
} int dis[N],S,T;
bool bfs()
{
memset(dis,,sizeof(dis));
queue<int>q;
q.push(S);
dis[S]=; while(!q.empty())
{
int now=q.front();
q.pop();
int Size=edges[now].size(); for(int i=; i<Size; i++)
{
ss e=edg[edges[now][i]];
if(e.flow>&&dis[e.v]==)
{
dis[e.v]=dis[now]+;
q.push(e.v);
}
}
}
if(dis[T]==)
return ;
return ; }
int current[N];
int dfs(int now,int maxflow)
{
if(now==T)
return maxflow;
int Size=edges[now].size();
for(int i=current[now]; i<Size; i++)
{
current[now]=i;
ss &e=edg[edges[now][i]]; if(e.flow>&&dis[e.v]==dis[now]+)
{
int Flow=dfs(e.v,min(maxflow,e.flow)); if(Flow)
{
e.flow-=Flow;
edg[edges[now][i]^].flow+=Flow;
return Flow;
}
}
}
return ;
} int dinic()
{
int ans=,flow;
while(bfs())
{
memset(current,,sizeof(current));
while(flow=dfs(S,INT_MAX/))
ans+=flow;
}
return ans;
} struct orz //铁轨和人的统一结构体,value<0为人,value>0为铁轨
{
int value,number;
int x,y,sgn; void setxy(int a,int b)
{
x=a;
y=b;
if(!x)sgn=y>;
else sgn=x>;
}
}; int cross(int x1,int y1,int x2,int y2)//计算叉积
{
return (x1*y2-x2*y1);
} int compare(orz a,orz b,orz c)//计算极角
{
return cross((b.x-a.x),(b.y-a.y),(c.x-a.x),(c.y-a.y));
} bool cmp(orz a,orz b)
{
if(a.sgn!=b.sgn)return a.sgn<b.sgn;
orz c;//原点
c.x = ;
c.y = ;
if(compare(c,a,b)==)//计算叉积,函数在上面有介绍,如果叉积相等,按照X从小到大排序
return a.number>b.number;
else
return compare(c,a,b)<;
} bool point_on_line(orz a,orz b)
{
int d1=__gcd(abs(a.x),abs(a.y)),d2=__gcd(abs(b.x),abs(b.y));
return (a.x/d1==b.x/d2)&&(a.y/d1==b.y/d2);
} const long double epsss=1e-; struct Point
{
int x,y;
Point() {}
Point(int _x,int _y)
{
x=_x,y=_y;
}
};
struct Pointd
{
long double x,y;
Pointd() {}
Pointd(long double _x,long double _y)
{
x=_x,y=_y;
}
}; int cross(const Point&a,const Point&b)
{
return a.x*b.y-a.y*b.x;
} long double crossd(const Pointd&a,const Pointd&b)
{
return a.x*b.y-a.y*b.x;
} int sig(int x)
{
if(x==)
return ;
return x>?:-;
} int sigd(long double x)
{
if(fabs(x)<epsss)
return ;
return x>?:-;
} int distance_cmp(const orz&_a,const orz&_b,const orz&_c)//判断点a距离哪一条射线近
{
Point a(_a.x,_a.y);
Point b(_b.x,_b.y);
Point c(_c.x,_c.y);
Point d;
if(!cross(b,c))
{
d=Point(-b.y,b.x);
if(!cross(a,d))
return ;
if(sig(cross(d,a))==sig(cross(d,b)))
return -;
return ;
}
long double L=sqrt(b.x*b.x+b.y*b.y);
long double R=sqrt(c.x*c.x+c.y*c.y);
Pointd aa(a.x,a.y);
Pointd bb(b.x,b.y);
Pointd cc(c.x,c.y);
Pointd dd(d.x,d.y);
bb.x*=R;
bb.y*=R;
cc.x*=L;
cc.y*=L;
dd=Pointd(bb.x+cc.x,bb.y+cc.y);
if(!sigd(crossd(aa,dd)))
return ;
if(sigd(crossd(dd,aa))==sigd(crossd(dd,bb)))
return -;
return ;
} orz allpoint[N*];
int up[N],down[N]; int main()
{
int n,m;
scanf("%d %d",&n,&m);
S=n+m+;
T=n+m+; for(int i=; i<=n; i++)
{
int x,y;
scanf("%d %d",&x,&y);
allpoint[i].setxy(x,y);
allpoint[i].value=-;
allpoint[i].number=i;
addedge(S,i,);
} for(int i=; i<=m; i++)
{
int x,y,z;
scanf("%d %d %d",&x,&y,&z);
allpoint[i+n].setxy(x,y);
allpoint[i+n].value=z;
allpoint[i+n].number=i+n;
addedge(i+n,T,z);
} sort(allpoint+,allpoint++n+m,cmp);//对人和铁轨一起进行极角排序 for(int i=n+m;i>=;i--)if(allpoint[i].value>=){down[]=i;break;} //寻找最后一个铁轨
for(int i=;i<=n+m;i++)
{
down[i]=down[i-];
if(allpoint[i].value>=)down[i]=i;
} for(int i=;i<=n+m;i++)if(allpoint[i].value>=){up[n+m+]=i;break;}//寻找第一个铁轨
for(int i=n+m;i>=;i--)
{
up[i]=up[i+];
if(allpoint[i].value>=)up[i]=i;
} for(int i=;i<=n+m;i++)
if(allpoint[i].value<)
{
int a=up[i],b=down[i]; if(a==b)addedge(allpoint[i].number,allpoint[a].number,);
else
if(point_on_line(allpoint[i],allpoint[a]))addedge(allpoint[i].number,allpoint[a].number,);
else
if(point_on_line(allpoint[i],allpoint[b]))addedge(allpoint[i].number,allpoint[b].number,);
else
{
int t=distance_cmp(allpoint[i],allpoint[a],allpoint[b]);
if(t<=)addedge(allpoint[i].number,allpoint[a].number,);
if(t>=)addedge(allpoint[i].number,allpoint[b].number,);
}
} int sum=dinic();
printf("%d\n",sum);
for(int i=; i<=n; i++)
{
int Size=edges[i].size();
for(int j=; j<Size; j++)
{
if(edg[edges[i][j]^].flow&&edg[edges[i][j]].v!=S)//注意这里要判一下另一个点是不是起点
{
printf("%d %d\n",i-,edg[edges[i][j]].v-n-);
break;
}
}
}
return ;
}
 

Hubtown的更多相关文章

  1. Hubtown(最大流)

    Hubtown 时间限制: 1 Sec  内存限制: 128 MB提交: 23  解决: 11[提交] [状态] [讨论版] [命题人:admin] 题目描述 Hubtown is a large N ...

  2. 2017-2018 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2017)

    A. Airport Coffee 设$f_i$表示考虑前$i$个咖啡厅,且在$i$处买咖啡的最小时间,通过单调队列优化转移. 时间复杂度$O(n)$. #include<cstdio> ...

  3. 2017-2018 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2017) Solution

    A - Airport Coffee 留坑. B - Best Relay Team 枚举首棒 #include <bits/stdc++.h> using namespace std; ...

随机推荐

  1. halcon相机标定及图像矫正

    https://blog.csdn.net/humanking7/article/details/44756073 相机标定内容详解:转载自 祥的博客 预备知识 标定中的四个坐标系 1.1.平面旋转 ...

  2. App Store上的开源应用汇总

    以下是互联网上主要的开源iOS应用的列表,在学习的时候,多看看完成的功能代码可以给我们带来很多经验,但是除了Apple官方提供的Sample Code之外,我们很难找到优质的开源项目代码,所以我搜集了 ...

  3. jni log 使用

    1. 在源文件中添加头文件 #include <android/log.h> #define LOG_TAG "System.out.c" #define LOGD(. ...

  4. QT5:第八章 信号与槽机制

    一.简介 QT编程中信号与槽用于处理界面各个组件的交互,类似与MFC的消息循环和绑定 注意:在使用信号与槽的类中,必须在类的定义中加入宏定义Q_OBJECT 信号(Signal)就是在特定情况下被发射 ...

  5. tomcat假死现象(转)

    1.1 编写目的 为了方便大家以后发现进程假死的时候能够正常的分析并且第一时间保留现场快照. 1.2编写背景 最近服务器发现tomcat的应用会偶尔出现无法访问的情况.经过一段时间的观察最近又发现有台 ...

  6. Eclipse调试:Run on server 和 Debug on server 区别

    Run on server: 以正常模式运行程序.会直接把程序从头到尾执行一遍,运行完就结束,不会进入到源代码里面(即使源代码中设置了断点). Debug on server: 以调试模式运行程序,或 ...

  7. 实体类和JSON对象之间相互转化

    . [代码]工具类 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 3 ...

  8. Spring框架bean的注解管理方法之一 使用注解生成对象

    首先在原有的jar包: 需Spring压缩包中的四个核心JAR包 beans .context.core 和expression 下载地址: https://pan.baidu.com/s/1qXLH ...

  9. 摘抄 Promise原理

    1.简单的promise: //极简promise雏形 function Promise(fn){ var value = null; callbacks = [];//callback为数组,因为可 ...

  10. Python内置方法详解

    1. 字符串内置方法详解 为何要有字符串?相对于元组.列表等,对于唯一类型的定义,字符串具有最简单的形式. 字符串往往以变量接收,变量名. 可以查看所有的字符串的内置方法,如: 1> count ...