Description

经过连续若干年的推广,Risk这个游戏已经风靡全国,成为大众喜闻乐见的重要娱乐方式。Risk这个游戏可以理解为一种简易的策略游戏,游戏者的目的是占领所有的土地。由于游戏规则的规定,只要两个国家相邻,就认为两个国家有交战的可能性。我们现在希望知道在当前的局面下,哪些国家之间有交战的可能性。注意,我们认为只有当两个国家的国界线有公共边的时候才认为相邻,若两个国家的领土只有公共点,则认为两个国家不相邻。每一个国家的边界由一系列线段组成,保证这个边界是一个简单多边形,即严格不自交。为了定位每个国家的位置,我们还给出每个国家最庞大的一支军队的位置,保证这个位置一定出现在某一个形内,而不是出现在某条边界上。

Input

输入文件的第一行中包括两个整数n,m。分别表示地图上的国家数和描述国家的边界的线段的数量。1<=n<=600,1<=m<=4000。接下来n行,每行用一对数描述了某个国家的主力军队的坐标。接下来m行,每行有4个数x1,y1,x2,y2,(x1,y1)-(x2,y2)描述了一条国界线。所有点的坐标都是0-10000之间的整数。保证输入的所有线段至多只会在线段交点处相交。整张地图上有且仅有一块面积无限的空白区域不属于任何国家。每一条国界线两侧的区域或者隶属于两个不同的国家,或者分隔了一个国家与那块无穷大的空白区域。即保证一条国界线两侧的区域不同时属于同一个国家或是同时都是空白区域。所有封闭区域内部包含且仅包含一支主力军队,表示了该区域的归属。  例如上图中第一行的数据是合法的。而第二行中的数据都是不合法的。左边的那幅图包含线段两侧都是空白区域;中间的图包含线段两侧区域同时属于同一个国家;右边的图中军队被布置在了国界线上,因此非法;此外若最右侧的图中若没有军队也是非法的。保证输入文件提供的数据都是合法的,你的程序不需要进行数据合法性的判定。

Output

包括n行,每行第一个数字x表示有x个国家可能与这个国家交战,接着在同一行中升序输出x个整数,表示可能与这个国家交战的国家的编号。国家按输入中给出的顺序从1到n编号。注意数字间严格以一个空格隔开,并且不要在行末输出多余的空白字符。

Sample Input

4 12
3 2
11 8
12 17
1 19
0 0 10 0
10 0 20 0
20 0 20 10
20 10 20 20
20 20 10 20
10 20 0 20
0 20 0 10
0 10 0 0
10 0 10 10
0 10 10 10
20 10 10 10
10 20 10 10

Sample Output

2 2 4
2 1 3
2 2 4
2 1 3

HINT

 

Source

Cerc2004

一个点定位裸题。

首先,我们要用最小左转法去找平面(将边拆成两条,按照极角序排序,每走到一个点就走他所走过的边的下一条边,直到走出一个环为止,所走出的边为一个平面)。但是有特殊情况要处理,比如平面套平面。因此,我们需要再处理一遍我们所得到的平面,消掉重复的平面。

然后,就是点定位了。点定位用扫描线平衡树离线做。将所有点按照x排序,从左往右扫过去:扫到一个做端点,将其所对应的线段加入平衡树中;扫到右端点,在平衡树中删除这条线段;扫到询问点,找到其所对应的高度的上一条线段。平衡树维护当前x坐标,线段的高低。实现有许多细节要处理,参见我写的code:

 #include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<queue>
#include<set>
using namespace std; #define rhl (100000)
#define oo (1e5)
#define eps (1e-6)
#define maxm (8010)
int n,m,all,sum,tot,pos[maxm],have[maxm],cnt = ;
double space[maxm]; bool exist[maxm]; inline bool equal(double x,double y) { return fabs(x-y) <= eps; }
inline bool dd(double a,double b) { if (equal(a,b)) return true; return a >= b; }
inline bool xd(double a,double b) { if (equal(a,b)) return true; return a <= b; } struct NODE
{
double x,y; friend inline bool operator == (NODE p,NODE q) { return equal(p.x,q.x)&&equal(p.y,q.y); }
friend inline NODE operator -(NODE p,NODE q) { return (NODE) {p.x-q.x,p.y-q.y}; }
friend inline double operator /(NODE a,NODE b) { return a.x*b.y-b.x*a.y; }
inline NODE ra()
{
int xx,yy;
do xx = rand()%rhl,yy = rand()%rhl;
while (equal(1.0*xx,x)||equal(1.0*yy,y));
return (NODE) {1.0*xx,1.0*yy};
}
inline double angle() { return atan2(y,x); }
inline void read() { scanf("%lf %lf",&x,&y); }
}pp[maxm],army[maxm]; struct LINE
{
double a,b,c;
inline bool on(NODE p) { return equal(,a*p.x+b*p.y+c); }
}; struct SEG
{
NODE a,b;
inline LINE extend() { return (LINE) {a.y-b.y,b.x-a.x,b.y*(a.x-b.x)-b.x*(a.y-b.y)}; }
inline bool on(NODE p)
{
if (p == a) return true;
if (p == b) return true;
return (dd(p.x,min(a.x,b.x))&xd(p.x,max(a.x,b.x)))&&(dd(p.y,min(a.y,b.y))&xd(p.y,max(a.y,b.y)));
}
}; struct EDGE
{
int from,to,id,sur; friend inline bool operator <(EDGE a,EDGE b) { return (pp[a.to]-pp[a.from]).angle() < (pp[b.to]-pp[b.from]).angle(); }
}edge[maxm]; struct SCAN
{
double x,y; int bel,sign; friend inline bool operator <(SCAN a,SCAN b)
{
if (a.x == b.x) return a.sign > b.sign;
return a.x < b.x;
}
}bac[maxm]; struct SPLAY
{
int num,root,ch[maxm][],fa[maxm],key[maxm]; queue <int> team; inline int newnode()
{
int ret;
if (team.empty()) ret = ++num;
else ret = team.front(),team.pop();
fa[ret] = ch[ret][] = ch[ret][] = ;
return ret;
} inline void init()
{
num = ; root = newnode();
key[root] = cnt;
} inline void rotate(int x)
{
int y = fa[x],z = fa[y],l = ch[y][] == x,r = l^;
if (z != ) ch[z][ch[z][] == y] = x;
fa[x] = z; fa[y] = x; fa[ch[x][r]] = y;
ch[y][l] = ch[x][r]; ch[x][r] = y;
fa[] = ;
} inline void splay(int x)
{
while (fa[x] != )
{
int y = fa[x],z = fa[y];
if (fa[y] != )
{
if ((ch[y][] == x)^(ch[z][] == y)) rotate(x);
else rotate(y);
}
rotate(x);
}
root = x;
} inline int lower_bound(NODE p)
{
int now = root,ret = ;
while (now)
{
int k = key[now];
if ((p-pp[edge[k].from])/(pp[edge[k].to]-pp[edge[k].from]) >= )
ret = k,now = ch[now][];
else now = ch[now][];
}
return ret;
} inline int find(int w)
{
int now = root;
double x = pp[edge[w].to].x,y = pp[edge[w].to].y;
double ang = (pp[edge[w].to] - pp[edge[w].from]).angle();
while (now)
{
int k = key[now];
if (k == w) return now;
NODE p = pp[edge[k].to] - pp[edge[k].from],q = pp[edge[k].from];
double xx = x - q.x,yy = q.y+xx/p.x*p.y;
if (equal(yy,y))
{
double t = p.angle();
now = ch[now][ang < t];
}
else now = ch[now][y > yy];
}
} inline void erase(int w)
{
int p = find(w);
while (ch[p][] || ch[p][])
{
if (ch[p][])
{
rotate(ch[p][]);
if (p == root) root = fa[p];
}
else
{
rotate(ch[p][]);
if (p == root) root = fa[p];
}
}
team.push(p);
ch[fa[p]][ch[fa[p]][] == p] = ;
fa[p] = ;
} inline void insert(int w)
{
int now = root,pre;
double x = pp[edge[w].from].x,y = pp[edge[w].from].y;
double ang = (pp[edge[w].to] - pp[edge[w].from]).angle();
double xx,yy;
while (true)
{
int k = key[now];
NODE p = pp[edge[k].to] - pp[edge[k].from],q = pp[edge[k].from];
xx = x - q.x,yy = q.y+xx/p.x*p.y;
if (equal(yy,y))
{
double t = p.angle();
pre = now,now = ch[now][ang > t];
if (!now)
{
now = newnode(); fa[now] = pre;
ch[pre][ang > t] = now; key[now] = w;
break;
}
}
else
{
pre = now,now = ch[now][y > yy];
if (!now)
{
now = newnode(); fa[now] = pre;
ch[pre][y>yy] = now; key[now] = w;
break;
}
}
}
splay(now);
}
}S;
vector <int> G[maxm],sp[maxm],ss[maxm],ans;
set <int> con[maxm]; inline void add(int a,int b)
{
++cnt; G[a].push_back(cnt);
edge[cnt] = (EDGE) {a,b};
} inline int find(NODE p)
{
for (int i = ;i <= all;++i) if (pp[i] == p) return i;
pp[++all] = p; return all;
} inline bool cmp(int a,int b) { return edge[a] < edge[b]; } inline NODE cp(LINE l1,LINE l2)
{
double a1 = l1.a,b1 = l1.b,c1 = l1.c;
double a2 = l2.a,b2 = l2.b,c2 = l2.c;
double ry = (c2*a1-c1*a2)/(b1*a2-b2*a1),rx = (c1*b2-c2*b1)/(b1*a2-b2*a1);
return (NODE) {rx,ry};
} inline bool para(LINE l1,LINE l2) { return equal(l1.a * l2.b,l1.b * l2.a); } inline bool okay(int a,int b)
{
int nn2 = sp[b].size(),nn1 = sp[a].size(),cro;
NODE p,q; SEG s,t; LINE l,l1;
for (int i = ;i < nn2;++i)
{
p = pp[sp[b][i]]; q = p.ra();
s = (SEG) {p,q}; l = s.extend();
cro = ;
for (int j = ;j < nn1;++j)
{
t = (SEG) {pp[sp[a][j]],pp[sp[a][(j+)%nn1]]};
l1 = t.extend();
if (l1.on(p)&&t.on(p)) return false;
if (para(l,l1)) continue;
q = cp(l,l1);
if (dd(q.x,p.x)&&t.on(q)) ++cro;
}
if (!(cro&)) return false;
}
return true;
} inline void find_surface()
{
for (int i = ;i <= all;++i) sort(G[i].begin(),G[i].end(),cmp);
for (int i = ;i <= all;++i)
{
int nn = G[i].size();
for (int j = ;j < nn;++j) edge[G[i][j]].id = j;
}
for (int i = ;i <= cnt;++i)
if (!edge[i].sur)
{
++tot; int j = i,p,nn; double res = ;
while (!edge[j].sur)
{
edge[j].sur = tot; p = edge[j].to;
sp[tot].push_back(p); ss[tot].push_back(j);
j ^= ; nn = G[p].size();
j = G[p][(edge[j].id+)%nn];
}
nn = sp[tot].size();
for (j = ;j < nn;++j)
res += (pp[sp[tot][j]]-pp[sp[tot][]])/(pp[sp[tot][(j+)%nn]]-pp[sp[tot][]]);
res /= ; space[tot] = res;
}
space[] = -1e12;
for (int i = ;i <= tot;++i)
if (!exist[i]&&space[i] > )
{
int in = ;
for (int j = ;j <= tot;++j)
if (!exist[j]&&space[j]<&&space[j]>space[in])
if (okay(j,i)) in = j;
if (in)
{
exist[i] = true;
int nn = ss[i].size();
for (int j = ;j < nn;++j) edge[ss[i][j]].sur = in;
}
}
for (int i = ;i <= cnt;i += )
{
if (space[edge[i].sur] <= &&space[edge[i^].sur] <= )
{
con[edge[i].sur].insert(edge[i^].sur);
con[edge[i^].sur].insert(edge[i].sur);
}
}
} inline void locate()
{
for (int i = ;i <= n;++i)
bac[++sum] = (SCAN) { army[i].x,army[i].y,i, };
for (int i = ;i <= cnt;i += )
{
if (equal(pp[edge[i].from].x,pp[edge[i].to].x)) continue;
bac[++sum] = (SCAN) { pp[edge[i].from].x,pp[edge[i].from].y,i, };
bac[++sum] = (SCAN) { pp[edge[i].to].x,pp[edge[i].to].y,i, };
}
pp[++all] = (NODE) {-oo,-oo}; pp[++all] = (NODE) {oo,-oo};
edge[++cnt] = (EDGE) {all-,all};
sort(bac+,bac+sum+);
S.init();
for (int i = ;i <= sum;++i)
{
if (bac[i].sign == ) pos[bac[i].bel] = edge[S.lower_bound((NODE) {bac[i].x,bac[i].y})].sur;
else if (bac[i].sign == ) S.insert(bac[i].bel);
else S.erase(bac[i].bel);
}
} inline void solve()
{
for (int i = ;i <= n;++i) have[pos[i]] = i;
for (int i = ;i <= n;++i)
{
ans.clear();
set <int> :: iterator it;
for (it = con[pos[i]].begin();it != con[pos[i]].end();++it)
ans.push_back(have[*it]);
sort(ans.begin(),ans.end());
int nn = ans.size();
printf("%d",nn);
for (int j = ;j < nn;++j) printf(" %d",ans[j]);
putchar('\n');
}
} int main()
{
freopen("1035.in","r",stdin);
freopen("1035.out","w",stdout);
srand();
scanf("%d %d",&n,&m);
for (int i = ;i <= n;++i) army[i].read();
for (int i = ;i <= m;++i)
{
NODE p,q; p.read(); q.read();
int a = find(p),b = find(q);
if (p.x > q.x) swap(a,b);
add(a,b); add(b,a);
}
find_surface();
locate();
solve();
fclose(stdin); fclose(stdout);
return ;
}

BZOJ 1035 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 2127: happiness [最小割]

    2127: happiness Time Limit: 51 Sec  Memory Limit: 259 MBSubmit: 1815  Solved: 878[Submit][Status][Di ...

  4. BZOJ 3275: Number

    3275: Number Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 874  Solved: 371[Submit][Status][Discus ...

  5. BZOJ 2879: [Noi2012]美食节

    2879: [Noi2012]美食节 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 1834  Solved: 969[Submit][Status] ...

  6. bzoj 4610 Ceiling Functi

    bzoj 4610 Ceiling Functi Description bzoj上的描述有问题 给出\(n\)个长度为\(k\)的数列,将每个数列构成一个二叉搜索树,问有多少颗形态不同的树. Inp ...

  7. BZOJ 题目整理

    bzoj 500题纪念 总结一发题目吧,挑几道题整理一下,(方便拖板子) 1039:每条线段与前一条线段之间的长度的比例和夹角不会因平移.旋转.放缩而改变,所以将每条轨迹改为比例和夹角的序列,复制一份 ...

  8. 【sdoi2013】森林 BZOJ 3123

    Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数.第三行包含N个非负整数 ...

  9. 【清华集训】楼房重建 BZOJ 2957

    Description 小A的楼房外有一大片施工工地,工地上有N栋待建的楼房.每天,这片工地上的房子拆了又建.建了又拆.他经常无聊地看着窗外发呆,数自己能够看到多少栋房子. 为了简化问题,我们考虑这些 ...

随机推荐

  1. cocoapods_第二篇

    一.什么是CocoaPods CocoaPods是iOS项目的依赖管理工具,该项目源码在Github上管理.开发iOS项目不可避免地要使用第三方开源库,CocoaPods的出现使得我们可以节省设置和第 ...

  2. 如何正确并完全安装Visual Studio 2015企业版本[转]

    http://blog.csdn.net/code_godfather/article/details/47381631 [注意事项]1> 本文描述的是: Visual Studio 2015企 ...

  3. [Javascript] Array - join()

    The join() method joins all elements of an array into a string. var name = 'shane osbourne'; var upp ...

  4. PHP拦截器的使用(转)

    PHP有如下几个拦截器: 1.__get($property)功能:访问未定义的属性是被调用2.__set($property, $value)功能:给未定义的属性设置值时被调用3.__isset($ ...

  5. Qt 学习之路 2(79):QML 组件

    前面我们简单介绍了几种 QML 的基本元素.QML 可以由这些基本元素组合成一个复杂的元素,方便以后我们的重用.这种组合元素就被称为组件.组件就是一种可重用的元素.QML 提供了很多方法来创建组件.不 ...

  6. GRADLE 构建最佳实践

    随着谷歌对Eclipse的无情抛弃和对Android Studio的日趋完善,使用gradle构建Android项目已经成为开发者的一项必会良技.那么,问题来了,采用什么样的姿势才能让项目开发构建过程 ...

  7. css培训(三)

    优先级 z-index 不设置 或auto   非static z-index :0 : z-index:-1: opacity 与层叠上下 opacity:.9 对其影响  小于1值   不具备堆叠 ...

  8. windows下安装redis和php的redis扩展

    1.redis简介 redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(s ...

  9. node.js中文资料导航

    以下资料来自gitHUb上面:https://github.com/youyudehexie/node123 Node.js HomePage Node官网七牛镜像 Infoq深入浅出Node.js系 ...

  10. 使用ListView 时,遇到了 Your content must have a ListView whose id attribute is 'android.R.id.list' 错误

    今天在开发Android小应用的时候,使用到了ListView,在使用自己创建的listview风格的时候,就出现了如标题的错误提示信息,这个就让我纳闷了,以前也不会出现这个问题啊,而且也可以运行,赶 ...