LOJ#2052. 「HNOI2016」矿区(对偶图)

题目描述


平面图&对偶图

平面图的定义:可以放在笛卡尔坐标系中,边只会在给定顶点处相交

把平面图中的每个区域+无穷域当作一个点,每条边变成两边的区域的连边,即为对偶图

把一条边拆成两条,每次找一个点,对于一条边找逆时针的下一条边(总方向是顺时针),直到找出一个环

因此要找一个块的内侧就可以按照上面的顺序寻找,找到的边所对应的块即为内侧

从u--v中u在v中的编号可以排序+二分,每条边只会经过一次

对于最小割可以把源汇连起来作为一个新的区域,从该块到无穷域的最短路即为最小割

题解

总面积很好求,问题是怎么求面积的平方和

把对偶图建一棵生成树,以无穷域为根统计子树和

无穷域找法:可以先判掉最外圈,具体为找y最大的点,然后逆时针寻找

也可以直接找面积为负的区域

如果一个询问经过的边不在生成树上就不管,如果内侧块(即当前边所对应的块,在顺时针方向)在生成树上为儿子就加上答案,否则减去答案

画一下就可以发现这样是对的

注意题目给出询问点是逆时针,所以要反过来

code

很好写(3.7k)

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
//#define file
using namespace std;

struct p{
	int x,y;
} a[200001];
struct type{
	int x,y;
	double s;
} b[1200001];
struct Type{
	int x,id;
} B[1200001];
int aa[800001][2];
int ls[400002];
bool bz[1200001];
int c[1200001];
int C[1200001];
int st[200002];
long long S[400002];
long long S2[400002];
int fa[400002];
int FA[400002];
int q[200002];
int n,m,Q,i,j,k,l,K,L,tot,N,Tot,mx,mx2,len,x,X,y;
long long lsans,ans1,ans2,Gcd;
char ch;

void New(int x,int y)
{
	++len;
	aa[len][0]=y;
	aa[len][1]=ls[x];
	ls[x]=len;
}

int getint()
{
	int x=0,k=1;
	
	ch=getchar();
	while (ch<'0' || ch>'9') k=(ch=='-')?-1:k,ch=getchar();
	while (ch>='0' && ch<='9') x=x*10+(ch-'0'),ch=getchar();
	
	return x*k;
}

long long cj(p a,p b)
{
	return b.x*a.y-a.x*b.y;
}

bool cmp(type a,type b) {return a.x<b.x || a.x==b.x && a.s<b.s;}
bool Cmp(Type a,Type b) {return a.x<b.x;}

double ang(p a,p b)
{
	int x=b.x-a.x,y=b.y-a.y;
	
	if (!y)
	{
		if (x>0)
		return 0;
		return M_PI;
	}
	if (!x)
	{
		if (y>0)
		return M_PI/2;
		return M_PI*3/2;
	}
	
	if (x>0 && y>0) return atan(1.0*y/x);
	if (x>0 && y<0) return atan(1.0*y/x)+M_PI*2;
	return atan(1.0*y/x)+M_PI;
}

long long gcd(long long a,long long b)
{
	long long r=a%b;
	
	while (r)
	a=b,b=r,r=a%b;
	
	return b;
}

int find(int t,int x)
{
	int l=st[t],r=st[t+1]-1,mid;
	
	while (l<r)
	{
		mid=(l+r)/2;
		
		if (B[mid].x<x)
		l=mid+1;
		else
		r=mid;
	}
	
	return B[l].id;
}

void work(int k,int l)
{
	int K,L;
	
	while (!bz[l])
	{
		S[N]+=cj(a[k],a[b[l].y]);
		
		bz[l]=1;
		c[l]=N;
		
		K=b[l].y;
		L=find(K,k);
		if (L==st[K+1]-1)
		L=st[K];
		else
		++L;
		
		k=K,l=L;
	}
	S2[N]=S[N]*S[N];
}

int gf(int t)
{
	if (fa[t]==t) return t;
	fa[t]=gf(fa[t]);
	return fa[t];
}

void dfs(int Fa,int t)
{
	int i;
	FA[t]=Fa;
	
	for (i=ls[t]; i; i=aa[i][1])
	if (aa[i][0]!=Fa)
	{
		dfs(t,aa[i][0]);
		S2[t]+=S2[aa[i][0]];
	}
}

int main()
{
	#ifdef file
	freopen("loj2052_8.in","r",stdin);
	#endif
	
	n=getint();m=getint();Q=getint();
	fo(i,1,n)
	a[i].x=getint(),a[i].y=getint();
	fo(i,1,m)
	{
		j=getint(),k=getint();
		
		b[++tot]={j,k,ang(a[j],a[k])};
		b[++tot]={k,j,ang(a[k],a[j])};
	}
	
	sort(b+1,b+tot+1,cmp);
	fo(i,1,tot) B[i]={b[i].y,i};
	
	j=1;
	fo(i,1,n)
	{
		st[i]=j;
		while (j<=tot && b[j].x==i)
		++j;
	}
	st[n+1]=tot+1;
	
	fo(i,1,n)
	if (st[i]<st[i+1])
	sort(B+st[i],B+(st[i+1]-1)+1,Cmp);
	
//	---外边界
	
	mx=-2133333333;
	fo(i,1,n)
	if (a[i].y>mx)
	mx=a[i].y,mx2=i;
	
	N=1;
	fo(j,st[mx2],st[mx2+1]-1)
	if (b[j].s>=M_PI-0.00000001)
	{
		work(mx2,j);
		break;
	}
	
//	---对偶图
	
	fo(i,1,n)
	{
		fo(j,st[i],st[i+1]-1)
		if (!bz[j])
		{
			++N;
			work(i,j);
		}
	}
	fo(i,1,N) fa[i]=i;
	
//	---生成树
	
	fo(i,1,n)
	{
		fo(j,st[i],st[i+1]-1)
		{
			k=find(b[j].y,i);
			C[j]=c[k];
			
			if (gf(c[j])!=gf(c[k]))
			{
				fa[fa[c[j]]]=fa[c[k]];
				bz[j]=bz[k]=0;
				
				New(c[j],c[k]);
				New(c[k],c[j]);
			}
		}
	}
	dfs(0,1);
	
//	---
	
	for (;Q;--Q)
	{
		lsans=ans1,ans1=ans2=0;
		Tot=getint();Tot=(Tot+lsans%n)%n+1;
		fo(i,1,Tot) q[i]=getint(),q[i]=(q[i]+lsans%n)%n+1;
		
		fd(i,Tot,1)
		{
			j=find(q[i%Tot+1],q[i]);
			
			if (!bz[j])
			{
				if (FA[c[j]]==C[j])
				ans1+=S2[c[j]];
				else
				if (FA[C[j]]==c[j])
				ans1-=S2[C[j]];
			}
			ans2+=cj(a[q[i%Tot+1]],a[q[i]]);
		}
		
		ans2*=2;
		Gcd=gcd(ans1,ans2);
		ans1/=Gcd,ans2/=Gcd;
		
		printf("%lld %lld
",ans1,ans2);
	}
}
原文地址:https://www.cnblogs.com/gmh77/p/12103683.html