题面

题解

知识引入

1. 平面图

一个图\(G=(V,E)\),若能将其画在平面上,且任意两条边的交点只能是\(G\)的顶点,则称\(G\)可嵌入平面,或称\(G\)是可平面的。

可平面图在平面上的一个嵌入称为一个平面图。如下图左边黑色的图为平面图,右边红色的图不属于平面图:

2. 平面图的对偶图

设有平面图\(G=(V,E)\),满足下列条件的图\(G'= (V',E')\)称为图\(G\)的对偶图:

\(G\)的任一面\(R_i\)内有且仅有一点\(V_i'\);对\(G\)的域\(R_i\)和\(R_j\)的共同边界\(E_k\),画一条边\(E_k'=(V_i',V_j')\)且只与\(E_k\)交于一点;若\(E_k\)完全处于\(R_i\)中,则\(V_i'\)有一自环\(E_k'\),如下图\(G'\)是\(G\)的对偶图:

本题题解

如何转对偶图,关键在于如何划分原图中的面,这个方法是先将双向边看成两条单向边,这样每一条边都属于一个面。

将每一条边按照极角排序,对于一条边\((s, t)\),我们在以\(t\)为起点的边中找到\((t, s)\),排序之后其上一条边就是当前面的下一条边界,这样一直找到整个区域闭合,就说明这个面上的边全部找出来了。这个步骤可以用vector存边。

建好了对偶图之后随意拿出一个生成树,以无边界的范围为根。

无边界的范围很好求,用叉积算出有向面积时,算出来是负数的就是无边界的范围。

然后标记所有的树边,记录生成树中每个子树的矿区面积和及面积平方和。

对于每一个询问,先找到询问里出现的边,如果有非树边就忽略,否则如果这条边所在的面是儿子,就加上子树的面积,如果是父亲就减去儿子子树的面积。

代码

#include<cstdio>
#include<cstring>
#include<cctype>
#include<cmath>
#include<algorithm>
#include<vector>
#define RG register
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define clear(x, y) memset(x, y, sizeof(x)) inline int read()
{
int data = 0, w = 1; char ch = getchar();
while(ch != '-' && (!isdigit(ch))) ch = getchar();
if(ch == '-') w = -1, ch = getchar();
while(isdigit(ch)) data = data * 10 + (ch ^ 48), ch = getchar();
return data * w;
} const int maxn(200010), maxm(1200010);
const double eps(1e-10);
int n, m, Q, cnt, root, e_num = 1, pos[maxm];
long long ans1, ans2;
struct point { int x, y; } p[maxn];
inline point operator - (const point &lhs, const point &rhs)
{ return (point) {lhs.x - rhs.x, lhs.y - rhs.y}; }
inline long long operator * (const point &lhs, const point &rhs)
{ return 1ll * lhs.x * rhs.y - 1ll * lhs.y * rhs.x; }
struct edge { int id, x, y; double ang; } e[maxm];
inline bool operator < (const edge &lhs, const edge &rhs)
{
return fabs(lhs.ang - rhs.ang) < eps ?
lhs.y < rhs.y : lhs.ang < rhs.ang;
} long long sn[maxm], sd[maxm];
int next[maxm], fa[maxm], vis[maxm], ist[maxm], qry[maxm];
std::vector<edge> g[maxn], T[maxm];
inline void add_edge(int x, int y)
{
++e_num; e[e_num] = (edge) {e_num, x, y,
atan2(p[y].y - p[x].y, p[y].x - p[x].x)};
g[x].push_back(e[e_num]);
} void build()
{
for(RG int i = 1; i <= n; i++) std::sort(g[i].begin(), g[i].end());
for(RG int i = 2; i <= e_num; i++)
{
int y = e[i].y; std::vector<edge>::iterator _e =
std::lower_bound(g[y].begin(), g[y].end(), e[i ^ 1]);
if(_e == g[y].begin()) _e = g[y].end();
--_e; next[i] = _e -> id;
}
for(RG int i = 2; i <= e_num; i++)
{
if(pos[i]) continue;
pos[i] = pos[next[i]] = ++cnt;
for(RG int j = next[i]; e[j].y != e[i].x; j = next[j], pos[j] = cnt)
sd[cnt] += (p[e[j].x] - p[e[i].x]) * (p[e[j].y] - p[e[i].x]);
if(sd[cnt] <= 0) root = cnt;
}
for(RG int i = 2; i <= e_num; i++)
T[pos[i]].push_back((edge) {i, pos[i], pos[i ^ 1]});
} void dfs(int x)
{
sn[x] = 1ll * sd[x] * sd[x], sd[x] <<= 1, vis[x] = 1;
for(RG int i = 0, sz = T[x].size(); i < sz; i++)
{
int y = T[x][i].y; if(vis[y]) continue;
ist[T[x][i].id] = ist[T[x][i].id ^ 1] = 1;
fa[y] = x; dfs(y); sd[x] += sd[y], sn[x] += sn[y];
}
} long long gcd(long long x, long long y)
{
while(y) y ^= x ^= y ^= x %= y;
return x;
} int main()
{
n = read(), m = read(), Q = read();
for(RG int i = 1; i <= n; i++) p[i] = (point) {read(), read()};
for(RG int i = 1, x, y; i <= m; i++) x = read(), y = read(),
add_edge(x, y), add_edge(y, x);
build(); dfs(root);
while(Q--)
{
int num = (read() + ans1) % n + 1;
for(RG int i = 1; i <= num; i++) qry[i] = (read() + ans1) % n + 1;
qry[num + 1] = qry[1], ans1 = ans2 = 0;
for(RG int i = 1; i <= num; i++)
{
int x = qry[i], y = qry[i + 1];
edge e = (edge) {0, x, y, atan2(p[y].y - p[x].y, p[y].x - p[x].x)};
std::vector<edge>::iterator _e =
std::lower_bound(g[x].begin(), g[x].end(), e);
int j = _e -> id; if(!ist[j]) continue;
if(fa[pos[j]] == pos[j ^ 1]) ans1 += sn[pos[j]], ans2 += sd[pos[j]];
else ans1 -= sn[pos[j ^ 1]], ans2 -= sd[pos[j ^ 1]];
}
long long tmp = gcd(ans1, ans2);
ans1 /= tmp, ans2 /= tmp;
printf("%lld %lld\n", ans1, ans2);
}
return 0;
}

【HNOI2016】矿区的更多相关文章

  1. [HNOI2016]矿区

    [HNOI2016]矿区 平面图转对偶图 方法: 1.分成正反两个单向边,每个边属于一个面 2.每个点按照极角序sort出边 3.枚举每一个边,这个边的nxt就是反边的前一个(这样找到的是面的边逆时针 ...

  2. 【LG3249】[HNOI2016]矿区

    [LG3249][HNOI2016]矿区 题面 洛谷 题解 先平面图转对偶图, 建好了对偶图之后随意拿出一个生成树,以无边界的范围为根. 无边界的范围很好求,用叉积算出有向面积时,算出来是负数的就是无 ...

  3. BZOJ 4541: [Hnoi2016]矿区 平面图转对偶图+DFS树

    4541: [Hnoi2016]矿区 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 433  Solved: 182[Submit][Status][ ...

  4. BZOJ4541 [Hnoi2016]矿区

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...

  5. 4541: [Hnoi2016]矿区

    学习了一下平面图剖分的姿势,orz cbh 每次只要随便选择一条边,然后不停尽量向左转就行 #include <bits/stdc++.h> #define N 1300000 #defi ...

  6. ●BZOJ 4541 [Hnoi2016]矿区

    题链: http://www.lydsy.com/JudgeOnline/problem.php?id=4541 题解: 平面图的对偶图,dfs树 平面图的对偶图的求法: 把所有双向边拆为两条互为反向 ...

  7. BZOJ4541 HNOI2016矿区(平面图转对偶图)

    考虑先将平面图转化为对偶图.具体地,将无向边拆成两条有向边.每次考虑找到包围一个区域的所有边.对当前考虑的边,找到该边的反向边在该边终点的出边集中,按极角序排序的后继,这条后继边也是包围该区域的边.这 ...

  8. [BZOJ4541][HNOI2016]矿区(平面图转对偶图)

    https://www.cnblogs.com/ljh2000-jump/p/6423399.html #include<cmath> #include<vector> #in ...

  9. 【bzoj4541】 Hnoi2016—矿区

    http://www.lydsy.com/JudgeOnline/problem.php?id=4541 (题目链接) 题意 给出一个平面图,若干询问,每次询问一个凸多边形内小多边形面积的平方和与面积 ...

  10. bzoj 4541: [Hnoi2016]矿区【平面图转对偶图+生成树】

    首先平面图转对偶图,大概思路是每条边存正反,每个点存出边按极角排序,然后找每条边在它到达点的出边中极角排序的下一个,这样一定是这条边所属最小多边形的临边,然后根据next边找出所有多边形,用三角剖分计 ...

随机推荐

  1. SVN switch 用法总结

    一直知道SVN有个switch命令,但是对它的介绍教程却很少,大多是生硬的svn帮助文档里的文字,从而一直不怎么会用.今天看了这篇文章,突觉豁然开朗,整理下来以备查阅. 使用SVN,自然是需要与别人合 ...

  2. linux安装activemq

    ActiveMQ是由Apache出品的,一款最流行的,能力强劲的开源消息总线.ActiveMQ是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现,它非常快速,支持多种语言 ...

  3. LeetCode 题解之Linked List Cycle II

    1.题目描述 2.问题分析 使用快慢指针方法判断链表是否有环,然后寻找环开始的节点. 3.代码 ListNode *detectCycle(ListNode *head) { if( head == ...

  4. LINUX使用extundelete进行数据恢复

    从删库到跑路不是一句笑话,不小心误删除操作很容易造成整个公司业务崩溃.所以真的误删除了,第一时间就是赶紧停机,不要再进行任何写入操作,把这块磁盘挂载到其他机器进行数据恢复 这里我们介绍一下使用 LIN ...

  5. 转:C#综合揭秘——细说多线程(下)

    原文地址:http://www.cnblogs.com/leslies2/archive/2012/02/08/2320914.html 引言 本文主要从线程的基础用法,CLR线程池当中工作者线程与I ...

  6. C语言中的数组与指针

    1. 数组的初始化 数组的初始化方法有很多,常用的方法有 定义时初始化 ]={,,}; 或 ]={}; //未初始化的元素全为0: 定义后遍历赋值初始化 int arr[3]; for(int i=0 ...

  7. Linux之JDK1.8的安装

    这个最基础的,但是老是查了一次又查,干脆记起来吧. 一.下载jdk8 地址:http://www.oracle.com/technetwork/java/javase/downloads/index. ...

  8. MySQL SELECT语句中只能输出1000行数据的原因

    同事反映,客户的一套MySQL生产库,执行SELECT.. INTO OUTFILE语句只能导出1000行 最初以为是系统参数被重新设置了,建议他更改系统参数 mysql> set global ...

  9. vuejs安装篇

    1.安装nodejs,自带npm环境. 地址:https://nodejs.org/en/download/,Node.js 历史版本下载地址:https://nodejs.org/dist/  可自 ...

  10. java使用elasticsearch进行模糊查询-已在项目中实际应用

    java使用elasticsearch进行模糊查询 使用环境上篇文章本人已书写过,需要maven坐标,ES连接工具类的请看上一篇文章,以下是内容是笔者在真实项目中运用总结而产生,并写的是主要方法和思路 ...