本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!

Description

  平面上的矿区划分成了若干个开发区域。简单地说,你可以将矿区看成一张连通的平面图,平面图划分为了若
干平面块,每个平面块即为一个开发区域,平面块之间的边界必定由若干整点(坐标值为整数的点)和连接这些整点
的线段组成。每个开发区域的矿量与该开发区域的面积有关:具体而言,面积为s的开发区域的矿量为 s^2。现在
有 m 个开采计划。每个开采计划都指定了一个由若干开发区域组成的多边形,一个开采计划的优先度被规定为矿
量的总和÷开发区域的面积和;例如,若某开采计划指定两个开发区域,面积分别为 a和b,则优先度为(a^2+b^2)
/(a+b)。由于平面图是按照划分开发区域边界的点和边给出的,因此每个开采计划也只说明了其指定多边形的边界
,并未详细指明是哪些开发区域(但很明显,只要给出了多边形的边界就可以求出是些开发区域)。你的任务是求
出每个开采计划的优先度。为了避免精度问题,你的答案必须按照分数的格式输出,即求出分子和分母,且必须是
最简形式(分子和分母都为整数,而且都消除了最大公约数;例如,若矿量总和是 1.5,面积和是2,那么分子应
为3,分母应为4;又如,若矿量和是 2,面积和是 4,那么分子应为 1,分母应为 2)。由于某些原因,你必须依
次对每个开采计划求解(即下一个开采计划会按一定格式加密,加密的方式与上一个开采计划的答案有关)。具体
的加密方式见输入格式。

Input

  第一行三个正整数 n,m,k,分别描述平面图中的点和边,以及开采计划的个数。接下来n行,第 i行(i=1,2,…
,n)有两个整数x_i, y_i,  表示点i的坐标为(x_i, y_i)。接下来m行,第 i行有两个正整数a,b,表示点a和b 之间
有一条边。接下来一行若干个整数,依次描述每个开采计划。每个开采计划的第一个数c指出该开采计划由开发区
域组成的多边形边界上的点的个数为d=(c+P) mod n + 1;接下来d个整数,按逆时针方向描述边界上的每一个点:
设其中第i个数为z_i,则第i个点的编号为(z_i+P) mod n + 1。其中P 是上一个开采计划的答案中分子的值;对于
第 1 个开采计划,P=0。

Output

  对于每个开采计划,输出一行两个正整数,分别描述分子和分母。

Sample Input

9 14 5
0 0
1 0
2 0
0 1
1 1
2 1
0 2
1 2
2 2
1 2
2 3
5 6
7 8
8 9
1 4
4 7
5 8
3 6
6 9
4 8
1 5
2 6
6 8
3 3 0 4 7 1 3 4 6 4 8 0 4 3 6 2 3 8 0 4 6 2 5 0 4 5 7 6 3

Sample Output

1 1
1 2
1 1
9 10
3 4

HINT

输入文件给出的9个点和14条边描述的平面图如下所示:

第一个开采计划,输入的第1个值为3,所以该开采计

划对应的多边形有(3+0) mod 8 +1=4个点,将接下的4个数3,0,4,7,分别代入(z_i+0) mod n + 1得到4个点的编号

为4,1,5,8。计算出第一个开采计划的分子为1,分母为1。类似地,可计算出余下开采计划的多边形的点数和点的

编号:第二个开采计划对应的多边形有3个点,编号分别为5, 6, 8。第三个开采计划对应的多边形有6个点,编号

分别为1, 2, 6, 5, 8, 4。第四个开采计划对应的多边形有5个点,编号分别为1, 2, 6, 8, 4。第五个开采计划对

应的多边形有6个点,编号分别为1, 5, 6, 8, 7, 4。

对于100%的数据,n, k ≤ 2×10^5, m ≤ 3n-6, |x_i|, |y

_i| ≤ 3×10^4。所有开采计划的d之和不超过2×10^6。保证任何开采计划都包含至少一个开发区域,且这些开发

区域构成一个连通块。保证所有开发区域的矿量和不超过 2^63-1。保证平面图中没有多余的点和边。保证数据合

法。由于输入数据量较大,建议使用读入优化。

正解:平面图转对偶图+树上统计

解题报告:

  这是我做的第一道计算几何大神题呀…

  考虑先把原图转成对偶图,转的方式就是,把无向边拆成两条有向边,然后对于每个点把他的所有出边极角排序,用vector的话很方便。

  然后我们就可以对于每条边算出从他出发的下一条要走的边是哪一条,二分预处理一下,记录下来。

  接下来就是扣域了!

  我从每一条没有归属的边出发,一路往下走,直到回到这条边的起点,把每条边标记为同一个域,我们可以在图上画画就能发现,扣出来的一定是这些边围出来的域。

  至于每次的下一条边,是极角序变大还是变小,就看你的定义了,其实是等价的。

  需要注意的是,我要用叉积顺便算一下每个域的面积,而面积为负数的域就是无穷域。

  然后我令无穷域为根节点,dfs一遍所有域,得到一棵对偶图的生成树,标记一下树边,并且统计子树的面积和以及面积平方和。

  对于每次询问,我只需要用像最开始找下一条边的方式找出相邻两个点之间的边的编号,如果是非树边就直接忽略,否则计算一下这条边所代表的儿子节点中的子树贡献。

  我们不妨规定一个方向,如果这条边时正方向就加上贡献,否则就减去,当然最后要取绝对值。

  

  我也不会证明呀...似乎可以用反证法来考虑,感觉一下还是可以意会的...

  有一个小trick:考虑我们叉积算出来的面积是四边形面积,本应该要/2,但是有可能会出现小数,所以我们先上下同时乘4就可以避免这个问题了...

//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
const int MAXN = 200011;
const int MAXM = 1200011;
int n,m,k,ecnt,next[MAXM],rt,belong[MAXM],cnt,father[MAXM]/*!!!*/,ask[MAXN];
bool vis[MAXM],in[MAXM];
LL s[MAXM],sp[MAXM],P,Q;
struct point{
int x,y;
inline point operator - (const point a) const { return (point){x-a.x,y-a.y}; }//向量,末-初
inline LL operator * (const point a) const { return 1LL*x*a.y-1LL*y*a.x; }//叉积
}p[MAXM]; struct edge{
int u,v,id;
double ang;
edge(){}
edge(int a,int b,int ii){ u=a; v=b; id=ii; ang=atan2(p[b].y-p[a].y,p[b].x-p[a].x); }
inline bool operator < (const edge a) const { return a.ang>ang; }
}e[MAXM]; vector<edge>w[MAXN],Tree[MAXM]; inline int getint(){
int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
} inline void link(int x,int y){ ecnt++; e[ecnt]=edge(x,y,ecnt); w[x].push_back(e[ecnt]); } inline LL gcd(LL x,LL y){ if(y==0) return x; return gcd(y,x%y); } inline int find(int x,const edge &bian){
int l=0,r=w[x].size()-1,mid;
while(l<r) {
mid=(l+r+1)>>1;/*!!!二分的方式*/
if(bian<w[x][mid]) r=mid-1;
else l=mid;
}
return l;
} inline void dfs(int x){
vis[x]=1;
sp[x]=s[x]*s[x]; s[x]<<=1;//考虑我们叉积算出来的面积是四边形面积,本应该要/2,但是有可能会出现小数,所以我们先上下同时乘4就可以避免这个问题了
for(int i=0,ss=Tree[x].size();i<ss;i++) {
int v=Tree[x][i].v; if(vis[v]) continue;
father[v]=x; in[Tree[x][i].id]=in[Tree[x][i].id^1]=1;//标记为树边
dfs(v); s[x]+=s[v]; sp[x]+=sp[v];//统计子树面积和,以及子树面积的平方和
}
} inline void work() {
n=getint(); m=getint(); k=getint();
ecnt=1; int x,y,nex;
for(int i=1;i<=n;i++) x=getint(),y=getint(),p[i]=(point){x,y};
for(int i=1;i<=m;i++) x=getint(),y=getint(),link(x,y),link(y,x);
for(int i=1;i<=n;i++) sort(w[i].begin(),w[i].end());//把每个点的出边按极角排序
for(int i=2;i<=ecnt;i++) {//预处理出每条边的反向边的极角序的下一条边,即走完这条边之后要走的下一条边
nex=find(e[i].v,e[i^1])-1;
if(nex==-1) nex=w[e[i].v].size()-1;//转一圈转了回来
next[i]=w[e[i].v][nex].id;//这条边走出去的下一条边
} for(int i=2;i<=ecnt;i++) {//处理每条边属于哪个域
if(!belong[i]) {
belong[i]=belong[next[i]]=++cnt;
for(int now=next[i];e[now].v!=e[i].u;now=next[now],belong[now]=cnt) {//直到回到原点
s[cnt]+=(p[e[now].u]-p[e[i].u])*(p[e[now].v]-p[e[i].u]);//叉积算面积,注意后者位于前者的逆时针方向才为正
}
if(s[cnt]<=0) rt=cnt;//无穷域
}
} for(int i=2;i<=ecnt;i++) Tree[belong[i]].push_back(edge(belong[i],belong[i^1],i));
dfs(rt); int d,now; P=Q=0;
while(k--) {
d=(getint()+P)%n+1;
for(int i=1;i<=d;i++) ask[i]=(getint()+P)%n+1;
ask[d+1]=ask[1]; P=Q=0;
for(int i=1;i<=d;i++) {
now=w[ask[i]][ find(ask[i],edge(ask[i],ask[i+1],0)) ].id;//找到当前边中的下一条边
if(!in[now]) continue;//考虑这条边在对偶图中的对应边,如果是非树边则不用考虑
if(father[belong[now]] == belong[now^1]) Q+=s[belong[now]],P+=sp[belong[now]];//考虑子树的统计,规定方向后,则只需对子树进行加加减减操作即可
else Q-=s[belong[now^1]],P-=sp[belong[now^1]];
}
LL gg=gcd(P,Q);
//LL gg=__gcd(P,Q);
P/=gg; Q/=gg;
printf("%lld %lld\n",P,Q);
}
//cout<<clock()<<endl;
} int main()
{
work();
return 0;
}

  

BZOJ4541 [Hnoi2016]矿区的更多相关文章

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

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

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

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

  3. [HNOI2016]矿区

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

  4. 【LG3249】[HNOI2016]矿区

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

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

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

  6. 【bzoj4541】 Hnoi2016—矿区

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

  7. 4541: [Hnoi2016]矿区

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

  8. ●BZOJ 4541 [Hnoi2016]矿区

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

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

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

随机推荐

  1. python---django中form组件(2)自定制属性以及表单的各种验证,以及数据源的实时更新,以及和数据库关联使用ModelForm和元类

    自定义属性以及各种验证 分析widget: class TestForm(forms.Form): user = fields.CharField( required = True, widget = ...

  2. bzoj千题计划202:bzoj3191: [JLOI2013]卡牌游戏

    http://www.lydsy.com/JudgeOnline/problem.php?id=3191 每个人获胜的概率只与其在排列中与庄家的相对位置有关 dp[i][j] 还剩i个人时,从庄家数第 ...

  3. NAT地址转换

    2017年1月12日, 星期四 NAT地址转换 SNAT:源地址转换  DNAT:目标地址转换   null

  4. html canvas非正方旋转和缩放...写的大多是正方的有人表示一直看正方的看厌了

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  5. 第5月第24天 线性变换 opengl

    1. http://news.qiyeku.com/news_837979.html 2. opengl + (Class)layerClass { return [CAEAGLLayer class ...

  6. Informatica学习:3、用户创建与权限管理

    环境:win7 下安装Informatica 9.6.1 服务器端与客户端作为学习之用,Linux大同小异 一.用户创建(服务器端) 1.登陆admin console (1)打开Admin Cons ...

  7. CentOS 无法通过 yum 安装新版 nodejs 解决办法(安装的还是老版的)

    官网安装说明:CentOS 安装 nodejs 第一步: curl --silent --location https://rpm.nodesource.com/setup_10.x | sudo b ...

  8. Regular Expression Matching & Wildcard Matching

    Regular Expression Matching Implement regular expression matching with support for '.' and '*'. '.' ...

  9. mac安装ocr

    mac安装Tesserocr 安装 Imagemagick 和 Tesseract 库: brew install imagemagick brew install tesseract --all-l ...

  10. WPF的EventAggregator的发布和订阅

    EventAggregator是Prism中专门处理ViewModel与ViewModel之间事件传递的类对象,它提供了针对事件的发布方法和订阅方法,所以可以非常方便的来管理事件.下面分几步来实现相关 ...