HNOI2016 矿区

矿区

平面上的矿区划分成了若干个开发区域。

简单地说,你可以将矿区看成一张连通的平面图,平面图划分为了若干平面块,每个平面块即为一个开发区域,平面块之间的边界必定由若干整点(坐标值为整数的点)和连接这些整点的线段组成。每个开发区域的矿量与该开发区域的面积有关:具体而言,面积为(s)的开发区域的矿量为(s^2)

现在有(m)个开采计划。每个开采计划都指定了一个由若干开发区域组成的多边形,一个开采计划的优先度被规定为矿量的总和÷开发区域的面积和;例如,若某开采计划指定两个开发区域,面积分别为(a)(b),则优先度为((a^2+b^2)/(a+b))。由于平面图是按照划分开发区域边界的点和边给出的,因此每个开采计划也只说明了其指定多边形的边界,并未详细指明是哪些开发区域(但很明显,只要给出了多边形的边界就可以求出是些开发区域)。

你的任务是求出每个开采计划的优先度。为了避免精度问题,你的答案必须按照分数的格式输出,即求出分子和分母,且必须是最简形式(分子和分母都为整数,而且都消除了最大公约数;

由于某些原因,你必须依次对每个开采计划求解(即下一个开采计划会按一定格式加密,加密的方式与上一个开采计划的答案有关)。具体的加密方式见输入格式。

对于 $ 100 % $ 的数据,$ n, k leq 2 imes 10^5, m leq 3n-6, |x_i|, |y_i| leq 3×10^4$。所有开采计划的 $ d $ 之和不超过 (2 imes 10^6)。保证任何开采计划都包含至少一个开发区域,且这些开发区域构成一个连通块。保证所有开发区域的矿量和不超过 $ 2^{63}-1 $。保证平面图中没有多余的点和边。保证数据合法。由于输入数据量较大,建议使用读入优化。

题解

https://www.cnblogs.com/heyujun/p/10447472.html

先平面图转对偶图,

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

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

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

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

这个可以通过画图手玩进行证明。

struct point {int64 x,y;};

IN point operator-(CO point&a,CO point&b){
	return {a.x-b.x,a.y-b.y};
}
IN int64 cross(CO point&a,CO point&b){
	return a.x*b.y-a.y*b.x;
}
IN float128 angle(CO point&a,CO point&b){
	return atan2((float128)b.y-a.y,b.x-a.x);
}

struct edge {int u,v;};

CO int N=2e6+10;
point p[N];
edge e[N];
map<pair<int,int>,int> loc;
float128 ang[N];
vector<int> to[N];
int nxt[N],adj[N];
array<int64,2> sum[N];

IN array<int64,2> operator+(CO array<int64,2>&a,CO array<int64,2>&b){
	return {a[0]+b[0],a[1]+b[1]};
}
IN array<int64,2> operator-(CO array<int64,2>&a,CO array<int64,2>&b){
	return {a[0]-b[0],a[1]-b[1]};
}

edge e2[N];
vector<int> to2[N];
int fa[N],vis[N],tree[N];

void dfs(int u){
	vis[u]=1;
	for(int i:to2[u]){
		int v=e2[i].v;
		if(vis[v]) continue;
		tree[i]=tree[i^1]=1;
//		cerr<<"tree "<<u<<" "<<v<<endl;
		fa[v]=u,dfs(v);
		sum[u]=sum[u]+sum[v];
	}
}
int main(){
	int n=read<int>(),m=read<int>(),Q=read<int>();
	for(int i=1;i<=n;++i) read(p[i].x),read(p[i].y);
	for(int i=1;i<=m;++i){
		int u=read<int>(),v=read<int>();
		e[i<<1]={u,v},ang[i<<1]=angle(p[u],p[v]),to[u].push_back(i<<1);
		e[i<<1|1]={v,u},ang[i<<1|1]=angle(p[v],p[u]),to[v].push_back(i<<1|1);
		loc[{u,v}]=i<<1,loc[{v,u}]=i<<1|1;
	}
	for(int i=1;i<=n;++i){
		sort(to[i].begin(),to[i].end(),[&](int a,int b)->bool{
			return ang[a]<ang[b];
		});
		for(int j=0;j<(int)to[i].size();++j)
			nxt[to[i][j]^1]=to[i][(j+to[i].size()-1)%to[i].size()];
	}
	int idx=0,root=-1;
	for(int i=2;i<=2*m+1;++i)if(!adj[i]){
		adj[i]=++idx;
//		cerr<<idx<<" face="<<endl;
//		cerr<<" ("<<p[e[i].u].x<<","<<p[e[i].u].y<<") -> ("<<p[e[i].v].x<<","<<p[e[i].v].y<<")"<<endl;
		int64 area=0;
		point st=p[e[i].u];
		for(int j=nxt[i];j!=i;j=nxt[j]){
			adj[j]=idx;
//			cerr<<" ("<<p[e[j].u].x<<","<<p[e[j].u].y<<") -> ("<<p[e[j].v].x<<","<<p[e[j].v].y<<")"<<endl;
			area+=cross(p[e[j].u]-st,p[e[j].v]-st);
		}
		if(area<0) root=idx;
		else sum[idx]={area*area,2*area}; // 4 times
	}
	
	for(int i=2;i<=2*m+1;++i){
		e2[i]={adj[i],adj[i^1]};
		to2[adj[i]].push_back(i);
	}
	dfs(root);
	array<int64,2> ans={};
	while(Q--){
		static int q[N];
		int c=(read<int64>()+ans[0])%n+1;
		for(int i=1;i<=c;++i) q[i]=(read<int64>()+ans[0])%n+1;
		q[c+1]=q[1];
		ans={0,0};
		for(int i=1;i<=c;++i){
			int id=loc[{q[i],q[i+1]}];
			if(!tree[id]) continue;
			int u=adj[id],v=adj[id^1];
//			cerr<<"side "<<u<<" "<<v<<endl;
			if(fa[u]==v) ans=ans+sum[u];
			else if(fa[v]==u) ans=ans-sum[v];
		}
		int64 g=gcd(ans[0],ans[1]);
		ans[0]/=g,ans[1]/=g;
		printf("%lld %lld
",ans[0],ans[1]);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/autoint/p/12782372.html