●BZOJ 4541 [Hnoi2016]矿区

题链:

http://www.lydsy.com/JudgeOnline/problem.php?id=4541

题解:

平面图的对偶图,dfs树
平面图的对偶图的求法:
把所有双向边拆为两条互为反向的单向边,
显然,每条单向边应该唯一属于一个平面。
我们依次枚举还没有被确定属于哪个平面的单向边,
然后从该边出发,包裹出一个最小的平面,那么途中所经过的边都属于这个平面。
包裹的具体做法:
对于当前的边x->y,我们找到其反向边y->x顺时针旋转遇到的第一条边y->z作为包裹该平面的下一条边。
重复上述操作,直到回到起点。
然后确定了每条边属于的平面,再建立关于平面与平面之间的边。
显而易见,新建的这些边一定会与原图中的边一一对应着垂直。
所以我们尽量要使得这些新边的编号和与它垂直的边的编号对应。
建立新边的具体做法:
对于枚举到的每一条边x->y,我们得到了其所属于的平面u,并令其反向边y->v所属于的平面v ,
然后建立u->v的边,并确定其编号就为当前枚举到的边x->y的编号。
以上就是求平面图的对偶图的做法。

然后再看看本题的询问:
由于每个询问包含了很多平面,要怎样才能快速获得这些平面的信息呢。
显然由给出的点集可以得到一个由很多边围成的区域,区域里的东西就是我们所需要的平面。
做法是对所有平面,以无穷域为根,随便建立一颗dfs树,并记录子树对应的所有平面的area的和以及area2的和。
然后那些围成区域的边就会把树的一些节点给包起来,那个直接统计被包起来的点的信息就好了。
关于如何得到那些信息:
遍历那些围成区域的边,如果该边对应的与它垂直的对偶图的边在树上,
就根据该对偶图的边是朝树上还是树下去加上子树信息或减去子树信息就好了。

代码:

#include<bits/stdc++.h>
#define MAXN 200005
#define MAXM 1200005
using namespace std;
struct Point{
	int x,y;
	Point(){}
	Point(int _x,int _y):x(_x),y(_y){}
}A[MAXN];
Point operator - (const Point &_A,const Point &_B){
	return Point(_A.x-_B.x,_A.y-_B.y);
}
long long operator ^ (const Point &_A,const Point &_B){
	return 1ll*_A.x*_B.y-1ll*_A.y*_B.x;
}
struct Segment{
	int x,y,id; double angle;
	Segment(){}
	Segment(int _x,int _y,int _id=0):x(_x),y(_y),id(_id){
		angle=atan2(A[y].y-A[x].y,A[y].x-A[x].x);
	}
	bool operator < (const Segment &rtm) const{
		return angle<rtm.angle;
	}
}e[MAXM];
vector<Segment>E[MAXN],G[MAXN*2];
long long area[MAXN*2],area2[MAXN*2];
int fa[MAXN*2];
int bel[MAXM],nxt[MAXM];
bool tag[MAXM];
long long ANS1,ANS2;
int N,M,K,dualroot,ent=2;
void read(int &x){
	static int sn; static char ch;
	x=0; sn=1; ch=getchar();
	for(;ch<‘0‘||‘9‘<ch;ch=getchar()) if(ch==‘-‘) sn=-1;
	for(;‘0‘<=ch&&ch<=‘9‘;ch=getchar()) x=x*10+ch-‘0‘;
	x=x*sn;
}
int find(int i,Segment v){
	return lower_bound(E[i].begin(),E[i].end(),v)-E[i].begin();
}
void dfs(int u){
	static bool vis[MAXN*2]; vis[u]=1;
	for(int i=0,v;i<(int)G[u].size();i++){
		v=G[u][i].y;
		if(vis[v]) continue;
		fa[v]=u; tag[G[u][i].id]=tag[G[u][i].id^1]=1;
		dfs(v); area[u]+=area[v];
		area2[u]+=area2[v];
	}
}
void dualgraph(){
 	int dnt=0;
	for(int i=2,st,tmp;i<ent;i++){
        if(bel[i]) continue;
		st=e[i].x; bel[i]=++dnt; tmp=i;
		while(1){
			tmp=nxt[tmp]; bel[tmp]=dnt;
			if(e[tmp].y==st) break;
			area[dnt]+=(A[e[tmp].x]-A[st])^(A[e[tmp].y]-A[st]);
		}
		if(area[dnt]<=0) dualroot=dnt;
		area2[dnt]=area[dnt]*area[dnt];
	}
	for(int i=2;i<ent;i++)
		G[bel[i]].push_back(Segment(bel[i],bel[i^1],i));
}
long long gcd(long long a,long long b){
	while(b^=a^=b^=a%=b);
	return a;
}
int decode(int x){return /*x;*/(x+ANS2)%N+1;}
void solve(){
	long long g;
	static int q[MAXN];
	int d,u,v,p,bu,bv;
	while(K--){
		read(d); d=decode(d);
		for(int i=1;i<=d;i++)
			read(q[i]),q[i]=decode(q[i]);
		q[d+1]=q[1]; ANS1=ANS2=0;
		for(int i=1;i<=d;i++){
			u=q[i]; v=q[i+1];
			p=find(u,Segment(u,v));
			p=E[u][p].id;
			if(!tag[p]) continue;
			bu=bel[p]; bv=bel[p^1];
			if(fa[bv]==bu) ANS2-=area2[bv],ANS1-=area[bv];
			else ANS2+=area2[bu],ANS1+=area[bu];
		}
		if(ANS1<0) ANS1*=-1,ANS2*=-1;
		ANS1*=2;
		g=gcd(ANS2,ANS1);
		ANS2/=g; ANS1/=g;
		printf("%lld %lld\n",ANS2,ANS1);
	}
}
int main(){
	read(N); read(M); read(K);
	for(int i=1;i<=N;i++) read(A[i].x),read(A[i].y);
	for(int i=1,x,y;i<=M;i++){
		read(x); read(y);
		e[ent]=Segment(x,y,ent);
		E[x].push_back(e[ent]); ent++;
		e[ent]=Segment(y,x,ent);
		E[y].push_back(e[ent]); ent++;
	}
	for(int i=1;i<=N;i++) sort(E[i].begin(),E[i].end());
	for(int i=2;i<ent;i++){
		int p=find(e[i].y,e[i^1])-1;
		if(p<0) p=E[e[i].y].size()-1;
		nxt[i]=E[e[i].y][p].id;
	}
	dualgraph();
	dfs(dualroot);
	solve();
	return 0;
}

  

原文地址:https://www.cnblogs.com/zj75211/p/8541592.html

时间: 2024-10-06 09:26:27

●BZOJ 4541 [Hnoi2016]矿区的相关文章

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

4541: [Hnoi2016]矿区 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 433  Solved: 182[Submit][Status][Discuss] Description 平面上的矿区划分成了若干个开发区域.简单地说,你可以将矿区看成一张连通的平面图,平面图划分为了若 干平面块,每个平面块即为一个开发区域,平面块之间的边界必定由若干整点(坐标值为整数的点)和连接这些整点 的线段组成.每个开发区域的矿量与该开发区域的面积有关:具

4541: [Hnoi2016]矿区

学习了一下平面图剖分的姿势,orz cbh 每次只要随便选择一条边,然后不停尽量向左转就行 #include <bits/stdc++.h> #define N 1300000 #define M 5000013 #define LL long long #define pb push_back using namespace std; LL n, m, k; struct point { LL x, y; } S[N]; vector <LL> bi[N]; vector <

bzoj4541 [Hnoi2016]矿区

Description 平面上的矿区划分成了若干个开发区域.简单地说,你可以将矿区看成一张连通的平面图,平面图划分为了若干平面块,每个平面块即为一个开发区域,平面块之间的边界必定由若干整点(坐标值为整数的点)和连接这些整点的线段组成.每个开发区域的矿量与该开发区域的面积有关:具体而言,面积为s的开发区域的矿量为 s^2.现在有 m 个开采计划.每个开采计划都指定了一个由若干开发区域组成的多边形,一个开采计划的优先度被规定为矿量的总和÷开发区域的面积和:例如,若某开采计划指定两个开发区域,面积分别

BZOJ 4537: [Hnoi2016]最小公倍数

4537: [Hnoi2016]最小公倍数 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 1084  Solved: 400[Submit][Status][Discuss] Description 给定一张N个顶点M条边的无向图(顶点编号为1,2,…,n),每条边上带有权值.所有权值都可以分解成2^a*3^b的形式.现在有q个询问,每次询问给定四个参数u.v.a和b,请你求出是否存在一条顶点u到v之间的路径,使得路径依次经过的边上的权值的最小公

bzoj 4538: [Hnoi2016]网络

Description 一个简单的网络系统可以被描述成一棵无根树.每个节点为一个服务器.连接服务器与服务器的数据线则看做一条树边.两个服务器进行数据的交互时,数据会经过连接这两个服务器的路径上的所有服务器(包括这两个服务器自身).由于这条路径是唯一的,当路径上的某个服务器出现故障,无法正常运行时,数据便无法交互.此外,每个数据交互请求都有一个重要度,越重要的请求显然需要得到越高的优先处理权.现在,你作为一个网络系统的管理员,要监控整个系统的运行状态.系统的运行也是很简单的,在每一个时刻,只有可能

bzoj4541【HNOI2016】矿区

4541: [Hnoi2016]矿区 Time Limit: 30 Sec  Memory Limit: 512 MB Submit: 282  Solved: 123 [Submit][Status][Discuss] Description 平面上的矿区划分成了若干个开发区域.简单地说,你可以将矿区看成一张连通的平面图,平面图划分为了若 干平面块,每个平面块即为一个开发区域,平面块之间的边界必定由若干整点(坐标值为整数的点)和连接这些整点 的线段组成.每个开发区域的矿量与该开发区域的面积有关

【bzoj】4538: [Hnoi2016]网络

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4538 维护一个数据结构支持对于一颗树的操作,需要支持: 1.对于树上的一条路径上的每个点上放一个值. 2.撤销某次操作的路劲放. 3.查询除了经过这个点的路径的最大值. 往一个路径上丢值相当于往不经过条路径的所有点上丢值. 用一个树链剖分即可维护,对于操作区间取反. 直接查询单点最大值即可. 为了维护单点最大值,线段树中的每一个点对应两个堆,用于维护插入誉删除. 防止爆空间,所以标记永久

【BZOJ】4542: [Hnoi2016]大数

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4542 给定一个由数字构成的字符串${S_{1,2,3,...,n}}$,一个正素数$P$,每次询问给定一对$l$,$r$求: $${\sum_{l=1}^{n}\sum_{r=i}^{n}\left [ \sum _{i=l}^{r}S[i]*10^{r-i} \,\,\,\,MOD\,\,\,\,P=0 \right ]}$$ 即以位置$x$开头的后缀的数字$%P$之后的值为$val[

【BZOJ4540】【HNOI2016】序列(莫队)

[BZOJ4540][HNOI2016]序列(莫队) 题面 BZOJ 洛谷 Description 给定长度为n的序列:a1,a2,-,an,记为a[1:n].类似地,a[l:r](1≤l≤r≤N)是指序列:al,al+1,-,ar- 1,ar.若1≤l≤s≤t≤r≤n,则称a[s:t]是a[l:r]的子序列.现在有q个询问,每个询问给定两个数l和r,1≤l≤r ≤n,求a[l:r]的不同子序列的最小值之和.例如,给定序列5,2,4,1,3,询问给定的两个数为1和3,那么a[1:3]有 6个子序