码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj4541【HNOI2016】矿区

时间:2016-06-14 01:11:39      阅读:237      评论:0      收藏:0      [点我收藏+]

标签:

4541: [Hnoi2016]矿区

Time Limit: 30 Sec  Memory Limit: 512 MB
Submit: 282  Solved: 123
[Submit][Status][Discuss]

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。保证平面图中没有多余的点和边。保证数据合

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




平面图转对偶图+DFS树

平面图转对偶图模板第一次写,全是抄的......

对偶图建好后,以无穷域为根建出DFS树,其中无穷域的判定是有向面积为负。

对于一个开采计划,一定包含对偶图中的某些边,并且这些边圈出对偶图的一个点集,这些点就是答案包含的点。答案就等于这些点的点权和,因为对偶图的点权对应平面图的面积。

记录DFS树每个子树的点权和,那么对于每一条树边判断在树上是朝上还是朝下,朝上就将答案加上子树的点权和,朝下就减去。如果是非树边直接忽略就好了。这个思路很妙啊!




#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define N 200005
#define M 600005
using namespace std;
int n,m,k,cnt=1,tot,rt;
int nxt[N*6],num[N*6],q[N],fa[N*2];
ll ans1,ans2;
ll s[N*2],s2[N*2];
bool vst[N*2],flg[N*6];
struct P
{
	ll x,y;
	friend P operator +(const P &a,const P &b){return (P){a.x+b.x,a.y+b.y};}
	friend P operator -(const P &a,const P &b){return (P){a.x-b.x,a.y-b.y};}
	friend ll operator *(const P &a,const P &b){return a.x*b.y-a.y*b.x;}
}p[N];
struct edge
{
	int x,y,id;double angle;
	edge(int xx=0,int yy=0,int idd=0)
	{
		x=xx;y=yy;id=idd;
		angle=atan2((double)(p[y].y-p[x].y),(double)(p[y].x-p[x].x));
	}
	friend bool operator <(const edge &a,const edge &b){return a.angle<b.angle;}
}e[N*6];
vector<edge> E[N],G[N*2];
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline int find(int x,const edge &tmp)
{
	return lower_bound(E[x].begin(),E[x].end(),tmp)-E[x].begin();
}
inline ll gcd(ll x,ll y)
{
	return y?gcd(y,x%y):x;
}
void solve()//平面图转对偶图 
{
	int now,tmp,st;ll area;
	F(i,2,cnt) if (!num[i])
	{
		area=0;now=i;st=e[i].x;num[i]=++tot;
		while (1)
		{
			int tmp=nxt[now];num[tmp]=tot;
			if (e[tmp].y==st) break;
			area+=(p[e[tmp].x]-p[st])*(p[e[tmp].y]-p[st]);
			now=tmp;
		}
		s[tot]=area;s2[tot]=area*area;
		if (area<=0) rt=tot;
	}
	F(i,2,cnt) G[num[i]].push_back(edge(num[i],num[i^1],i));
}
void dfs(int x)
{
	vst[x]=true;
	F(i,0,(int)G[x].size()-1)
	{
		int y=G[x][i].y;
		if (vst[y]) continue;
		fa[y]=x;
		flg[G[x][i].id]=flg[G[x][i].id^1]=true;
		dfs(y);
		s[x]+=s[y];s2[x]+=s2[y];
	}
}
int main()
{
	n=read();m=read();k=read();
	F(i,1,n) p[i].x=read(),p[i].y=read();
	F(i,1,m)
	{
		int x=read(),y=read();
		cnt++;e[cnt]=edge(x,y,cnt);E[x].push_back(e[cnt]);
		cnt++;e[cnt]=edge(y,x,cnt);E[y].push_back(e[cnt]);
	}
	F(i,1,n) sort(E[i].begin(),E[i].end());
	F(i,2,cnt)
	{
		int tmp=find(e[i].y,e[i^1])-1;
		if (tmp<0) tmp+=E[e[i].y].size();
		nxt[i]=E[e[i].y][tmp].id;
	}
	solve();
	dfs(rt);
	F(i,1,k)
	{
		int tot=(read()+ans2)%n+1;
		F(j,1,tot) q[j]=(read()+ans2)%n+1;
		ans1=ans2=0;q[tot+1]=q[1];
		F(j,1,tot)
		{
			int x=q[j],y=q[j+1],tmp=E[x][find(x,edge(x,y,0))].id;
			if (!flg[tmp]) continue;
			if (num[tmp]==fa[num[tmp^1]]) ans1+=s[num[tmp^1]],ans2+=s2[num[tmp^1]];
			else ans1-=s[num[tmp]],ans2-=s2[num[tmp]];
		}
		if (ans1<0) ans1=-ans1,ans2=-ans2;
		ll d=gcd(ans1,ans2);ans1/=d;ans2/=d;
		if (ans2&1) ans1<<=1;else ans2>>=1;
		printf("%lld %lld\n",ans2,ans1);
	}
	return 0;
}


bzoj4541【HNOI2016】矿区

标签:

原文地址:http://blog.csdn.net/aarongzk/article/details/51661524

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!