首先要将这个图连通,方法是通过扫描线+set求出每个连通块最高的点上方的第一条边,然后向交点连边。

然后把边拆成两条双向边,每次找到一条没走过的边,找到极角排序后它的反向边的后继,直到回到这条边。根据叉积可以求出面积,如果面积非负,那么就说明找到了一个封闭区域。

然后再进行一次扫描线,找到一个点上方最低的边,即可完成点定位。

时间复杂度$O(m\log m)$。

#include<cstdio>
#include<cmath>
#include<set>
#include<map>
#include<algorithm>
using namespace std;
const double eps=1e-8;
const int N=20010,M=50010;
int n,m,q,cnt,i,x,y;
map<int,int>T[20010];
inline int sgn(double x){
if(fabs(x)<eps)return 0;
return x>0?1:-1;
}
struct P{
double x,y;
P(){}
P(double _x,double _y){x=_x,y=_y;}
double operator*(const P&b){return x*b.y-y*b.x;}
}a[N],b[N];
struct E{
int x,y;double o;
E(){}
E(int _x,int _y){x=_x,y=_y,o=atan2(a[y].x-a[x].x,a[y].y-a[x].y);}
}e[M];
bool del[M],ex[M];int from[M],id[N],f[N];
struct EV{
double x;int y,t;
EV(){}
EV(double _x,int _y,int _t){x=_x,y=_y,t=_t;}
}ev[M<<1];
inline bool cmpEV(const EV&a,const EV&b){
if(sgn(a.x-b.x))return a.x<b.x;
return a.t<b.t;
}
namespace GetArea{
struct cmp{bool operator()(int a,int b){return e[a].o<e[b].o;}};
set<int,cmp>g[N];set<int,cmp>::iterator k;int i,j,q[M],t;
void work(){
for(i=0;i<m+m;i++)if(!del[i]&&!ex[i]){
for(q[t=1]=j=i;;q[++t]=j=*k){
k=g[e[j].y].find(j^1);k++;
if(k==g[e[j].y].end())k=g[e[j].y].begin();
if(*k==i)break;
}
double s=0;
for(j=1;j<=t;j++)s+=a[e[q[j]].x]*a[e[q[j]].y],del[q[j]]=1;
if(sgn(s)<0)continue;
for(cnt++,j=1;j<=t;j++)from[q[j]]=cnt;
}
}
}
namespace ScanLine{
struct cmp{
bool operator()(int A,int B){
if(e[A].x==e[B].x)return e[A].o>e[B].o;
double x=min(a[e[A].x].x,a[e[B].x].x),
yA=(a[e[A].x].y-a[e[A].y].y)*(x-a[e[A].y].x)/(a[e[A].x].x-a[e[A].y].x)+a[e[A].y].y,
yB=(a[e[B].x].y-a[e[B].y].y)*(x-a[e[B].y].x)/(a[e[B].x].x-a[e[B].y].x)+a[e[B].y].y;
return yA>yB;
}
};
set<int,cmp>T;
int cnt,i,j,k,g[M],v[M],nxt[M],ed,vis[N],t,tmp[N];
inline bool cmpC(int x,int y){return a[x].x<a[y].x;}
inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
void dfs(int x){
vis[x]=1;
if(a[x].y>a[t].y)t=x;
for(int i=g[x];i;i=nxt[i])if(!vis[v[i]])dfs(v[i]);
}
inline double cal(int A,double x){
return(a[e[A].x].y-a[e[A].y].y)*(x-a[e[A].y].x)/(a[e[A].x].x-a[e[A].y].x)+a[e[A].y].y;
}
void connect(){
for(i=0;i<m+m;i++)add(e[i].x,e[i].y);
for(i=1;i<=n;i++)if(!vis[i])dfs(t=i),ev[cnt++]=EV(a[t].x,t,2);
for(i=0;i<m+m;i++)if(sgn(a[e[i].x].x-a[e[i].y].x)>0){
ev[cnt++]=EV(a[e[i].y].x,i,1);
ev[cnt++]=EV(a[e[i].x].x,i,0);
}
sort(ev,ev+cnt,cmpEV);
a[n+1]=P(10010,10010);
a[n+2]=P(-10010,10010);
e[m+m]=E(n+1,n+2);
T.insert(m+m);
e[m+m+1]=E(n+2,n+1);
n+=2,m++;
for(ed=0,i=1;i<=n;i++)g[i]=0;
for(i=0;i<cnt;i++){
if(ev[i].t==0)T.erase(ev[i].y);
if(ev[i].t==1)T.insert(ev[i].y);
if(ev[i].t==2){
a[n+1]=P(ev[i].x,a[ev[i].y].y+eps);
a[n+2]=P(ev[i].x-1,a[ev[i].y].y+eps);
e[m+m]=E(n+1,n+2);
T.insert(m+m);
set<int,cmp>::iterator j=T.find(m+m);
j--,add(*j,ev[i].y);
T.erase(m+m);
}
}
int newm=m+m;
for(i=0;i<m+m;i++){
for(cnt=0,j=g[i];j;j=nxt[j]){
if(!sgn(a[v[j]].x-a[e[i].x].x)){
e[newm++]=E(v[j],e[i].x);
e[newm++]=E(e[i].x,v[j]);
continue;
}
if(!sgn(a[v[j]].x-a[e[i].y].x)){
e[newm++]=E(v[j],e[i].y);
e[newm++]=E(e[i].y,v[j]);
continue;
}
tmp[++cnt]=v[j];
}
if(!cnt)continue;
ex[i]=ex[i^1]=1;
sort(tmp+1,tmp+cnt+1,cmpC);
for(k=e[i].y,j=1;j<=cnt;k=n,j++){
a[++n]=P(a[tmp[j]].x,cal(i,a[tmp[j]].x));
e[newm++]=E(k,n);
e[newm++]=E(n,k);
e[newm++]=E(tmp[j],n);
e[newm++]=E(n,tmp[j]);
}
e[newm++]=E(n,e[i].x);
e[newm++]=E(e[i].x,n);
}
m=newm/2;
}
void location(){
for(i=cnt=0;i<m+m;i++)if(!ex[i]&&sgn(a[e[i].x].x-a[e[i].y].x)>0){
ev[cnt++]=EV(a[e[i].y].x,i,1);
ev[cnt++]=EV(a[e[i].x].x,i,0);
}
for(i=0;i<q;i++)ev[cnt++]=EV(b[i].x,i,2);
sort(ev,ev+cnt,cmpEV);
T.clear();
for(i=0;i<cnt;i++){
if(ev[i].t==0)T.erase(ev[i].y);
if(ev[i].t==1)T.insert(ev[i].y);
if(ev[i].t==2){
a[n+1]=P(ev[i].x,b[ev[i].y].y);
a[n+2]=P(ev[i].x-1,b[ev[i].y].y);
e[m+m]=E(n+1,n+2);
T.insert(m+m);
set<int,cmp>::iterator j=T.find(m+m);
if(j!=T.begin())j--,id[ev[i].y]=from[*j];
T.erase(m+m);
}
}
}
}
inline int getid(){
int x,y;
scanf("%d%d",&x,&y);
if(T[x+10000][y])return T[x+10000][y];
T[x+10000][y]=++n;
a[n]=P(x,y);
return n;
}
bool g[610][610];
int main(){
scanf("%d%d",&q,&m);
for(i=0;i<q;i++)scanf("%lf%lf",&b[i].x,&b[i].y);
for(i=0;i<m;i++){
x=getid();
y=getid();
e[i<<1]=E(x,y);
e[i<<1|1]=E(y,x);
}
ScanLine::connect();
for(i=0;i<m+m;i++)if(!ex[i])GetArea::g[e[i].x].insert(i);
GetArea::work();
ScanLine::location();
for(i=0;i<q;i++)f[id[i]]=i+1;
for(i=0;i<m+m;i++)if(!ex[i])g[f[from[i]]][f[from[i^1]]]=1;
for(i=1;i<=q;i++){
for(x=g[i][i]=0,y=1;y<=q;y++)if(g[i][y])x++;
printf("%d",x);
for(y=1;y<=q;y++)if(g[i][y])printf(" %d",y);
puts("");
}
return 0;
}

  

BZOJ1035 : [ZJOI2008]Risk的更多相关文章

  1. bzoj 1030-1039

    1030 JSOI2007 文本生成器 AC自动机加DP即可. 1031 JSOI2007 字符加密Cipher 后缀数组即可. 1032 JSOI2007 祖码Zuma 数据有问题. 设\(f(l, ...

  2. bzoj AC倒序

    Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...

  3. BZOJ 1036: [ZJOI2008]树的统计Count [树链剖分]【学习笔记】

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 14302  Solved: 5779[Submit ...

  4. bzoj1036 [ZJOI2008]树的统计Count

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 12646  Solved: 5085 [Subm ...

  5. BZOJ 1036: [ZJOI2008]树的统计Count

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 14354  Solved: 5802 [Subm ...

  6. PHP Datatype Conversion Safety Risk、Floating Point Precision、Operator Security Risk、Safety Coding Principle

    catalog . 引言 . PHP operator introduction . 算术运算符 . 赋值运算符 . 位运算符 . 执行运算符 . 递增/递减运算符 . 数组运算符 . 类型运算符 . ...

  7. Linux fork()、exec() Hook Risk、Design-Principle In Multi-Threadeed Program

    目录 . Linux exec指令执行监控Hook方案 . 在"Multi-Threadeed Program"环境中调用fork存在的风险 . Fork When Multi-T ...

  8. UVa12264 Risk(最大流)

    题目 Source https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_pr ...

  9. 【BZOJ1036】[ZJOI2008]树的统计Count 树链剖分

    [BZOJ1036][ZJOI2008]树的统计Count Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. ...

随机推荐

  1. Iphone和iPad适配, 横竖屏

    竖屏情况下: [UIScreen mainScreen].bounds.size.width = 320 [UIScreen mainScreen].bounds.size.width = 568 横 ...

  2. DCMTK354之VC++ 2008 MFC应用程序配置完整过程

    花了一个礼拜,终于在VC++2008 MFC 应用程序中完成了首个基于DCMTK354的首个程序ECHOSCUWIN32,现将过程记录下来,便于日后查阅,同时也提供给那些有幸看到此博文而对他们又有帮助 ...

  3. zookeeper 用法和日常运维

    本文以ZooKeeper3.4.3版本的官方指南为基础:http://zookeeper.apache.org/doc/r3.4.3/zookeeperAdmin.html,补充一些作者运维实践中的要 ...

  4. windows下的C/C++精确计时

    由于我要测试线性筛法的速度,用上了C/C++精确计时.此时传统的clock()方法不够用了,我们需要另一种测量的办法,即CPUTicks/CPUFreq.如何实现呢? #include <win ...

  5. MySQL数据库索引的4大类型以及相关的索引创建

    以下的文章主要介绍的是MySQL数据库索引类型,其中包括普通索引,唯一索引,主键索引与主键索引,以及对这些索引的实际应用或是创建有一个详细介绍,以下就是文章的主要内容描述. (1)普通索引 这是最基本 ...

  6. Android ViewPager轮播图

    Android客户端开发中很多时候需要用到轮播图的方式进行重点新闻的推送或者欢迎页面的制作,下面这个轮播图效果的Deamo来自互联网再经过修改而成. 1.布局文件activity_main.xml中添 ...

  7. Delphi经验总结(2)

    Q: 怎么来改变ListBox的字体呢?就修改其中的一行. A: 先把ListBox1.Style 设成lbOwnerDrawFixed 然后在 OnDrawItem 事件下写下如下代码 proced ...

  8. Codeforces 389B(十字模拟)

    Fox and Cross Time Limit: 1000MS   Memory Limit: 262144KB   64bit IO Format: %I64d & %I64u Submi ...

  9. 组合数(codevs 1631)

    1631 组合数  时间限制: 1 s  空间限制: 256000 KB  题目等级 : 钻石 Diamond 题解  查看运行结果     题目描述 Description 组合数C(N, K)表示 ...

  10. JPush Wiki

    极光推送包含有通知与自定义消息两种类型的推送.本文描述他们的区别,以及建议的应用场景. 功能角度 通知 或者说 Push Notification,即指在手机的通知栏(状态栏)上会显示的一条通知信息. ...