HNOI2016 矿区 题解

[HNOI2016] 矿区 Solution

神仙计算几何加图论题。

题目大意:给定一个连通平面图,平面图被分成很多个面,每次询问一个多边形的贡献,贡献是其中每个面的面积的平方和除以面积和。

解题思路

我们需要把一个多边形内的每个面一起算,可以考虑对每个面之间连边,建出一棵生成树。

实际上就是平面图转成对偶图。对偶图:把原平面图的每个面看作一个点,把原图上有邻边的面在新图上对应的点连边。

首先我们需要找出每个面包含哪些边。对于一个点,把它的边按极角排序后,相邻的边就同属一个多边形。可以先枚举一条初始边,然后逆时针走向下一条极角比它小的边,直到走回初始边,这样就找到了一个面。
同时我们可以求这个多边形的面积与面积的平方,使用向量叉积求面积。由于我们是逆时针求,所以叉积是正的。

然后连边,首先每条边属于两个面,给这两个面连边即可。我们也可以知道面的个数是 \(O(m)\) 的。

假如我们已经建出了生成树,那么每次询问一个多边形时,可以想象这个生成树会穿过多边形,对于一条进入多边形的树边就加上子树和,对于一条走出多边形的树边就减去子树和,发现我们就可以处理询问了。

但我们发现,这要求树根在多边形外。而刚好,我们求面时,会求到原平面图外范围无限的面(绕边界顺时针走),且由于是顺时针走的,所以面积叉积是负的,我们能容易地找到它。我们以这个无限的面为根即可。可以一次 DFS 求出生成树。

时间复杂度 \(O((m+d)\log m)\)

细节

  • 叉积算面积需要除以二,而对应的面积的平方会除以四,我们可以在最后算答案时,给分母乘二即可。
  • 不要使用 mapunordered_map 查找一个点上连的边,或者查找两个点之间连的边,那样会 TLE。最好用 vector 存下来排序,然后二分查找。
const int N=2e5+5,M=6e5+5;
struct point{int x,y;};
int operator^(point a,point b){return a.x*b.y-b.x*a.y;}
int n,m,K,X[N],Y[N];
struct edge{int to,num; ld rad;};
int find(vector<edge> &x,ld rad){
	int l=0,r=x.size()-1,ans=0;
	while(l<=r){
		int mid=(l+r)>>1;
		if(x[mid].rad>rad)l=mid+1;
		else ans=mid,r=mid-1;
	}
	return ans;
}
vector<edge> G[N];
int tot,side[M*2],bz[M*2];
ll s1[M*2],s2[M*2];
int root,vis[M*2],fa[M*2];
vector<int> g[M*2];
void dfs(int x){
	vis[x]=1;
	for(int v:g[x])if(!vis[v]){
		fa[v]=x;
		dfs(v);
		s1[x]+=s1[v],s2[x]+=s2[v];
	}
}
ll gcd(ll x,ll y){return y?gcd(y,x%y):x;}
unordered_map<int,int> mp[N];
int add[M*2],del[M*2];
signed main(){
	read(n,m,K);
	fo(i,1,n)read(X[i],Y[i]);
	fo(i,1,m){
		int u,v; read(u,v);
		G[u].push_back((edge){v,i*2-1,atan2l(Y[v]-Y[u],X[v]-X[u])});
		G[v].push_back((edge){u,i*2,atan2l(Y[u]-Y[v],X[u]-X[v])});
		mp[u][v]=i*2-1,mp[v][u]=i*2;
	}
	fo(i,1,n)sort(G[i].begin(),G[i].end(),[](edge a,edge b){return a.rad>b.rad;});
	fo(i,1,n)for(auto j:G[i])if(!bz[j.num]) {
		bz[j.num]=1,side[j.num]=++tot;
		int x=j.to,from=i;
		s1[tot]+=point{X[i],Y[i]}^point{X[x],Y[x]};
		while(x!=i){
			int at=find(G[x],atan2l(Y[from]-Y[x],X[from]-X[x]));
			if(++at==G[x].size())at=0;
			bz[G[x][at].num]=1,side[G[x][at].num]=tot;
			from=x,x=G[x][at].to;
			s1[tot]+=point{X[from],Y[from]}^point{X[x],Y[x]};
		}
		if(s1[tot]<=0)root=tot;
		s2[tot]=s1[tot]*s1[tot];
	}
	fo(i,1,m){
		int u=side[i*2-1],v=side[i*2];
		g[u].push_back(v),g[v].push_back(u);
	}
	dfs(root);
	ll lastans=0;
	fo(i,1,K){
		int c; read(c);
		c=(c+lastans)%n+1;
		int d; read(d);
		d=(d+lastans)%n+1;
		int d1=d,t1=0,t2=0;
		fo(j,1,c-1){
			int t; read(t);
			t=(t+lastans)%n+1;
			int u=side[mp[d][t]],v=side[mp[t][d]];
			if(fa[u]==v)add[++t1]=u;
			else if(fa[v]==u)del[++t2]=v;
			d=t;
		}
		int u=side[mp[d][d1]],v=side[mp[d1][d]];
		if(fa[u]==v)add[++t1]=u;
		else if(fa[v]==u)del[++t2]=v;
		sort(add+1,add+t1+1),sort(del+1,del+t2+1);
		t1=unique(add+1,add+t1+1)-add-1,t2=unique(del+1,del+t2+1)-del-1;
		ll S1=0,S2=0;
		fo(i,1,t1)S1+=s1[add[i]],S2+=s2[add[i]];
		fo(i,1,t2)S1-=s1[del[i]],S2-=s2[del[i]];
		S1*=2;
		ll cd=gcd(S1,S2);
		S1/=cd,S2/=cd;
		write(S2,' ',S1,'\n');
		lastans=S2;
	}
	return 0;
}
posted @ 2024-12-28 07:49  dengchengyu  阅读(28)  评论(0)    收藏  举报