BZOJ 4541: [Hnoi2016]矿区

平面图转对偶图完全不会啊,只好学一下了

Orz了清哥的代码,感觉思路很清晰啊

顺手写个学习笔记吧。

方便起见编号为i的边与编号为i^1的边互为反向边。

对于每个点进行极角排序,同时用一个数组表示该边的下一条边。

于是乎我们从边i出发,到i的反向边的下一条边,画个图可以发现这么走形成的域一定在这些边的右边,并且他们之中仅包含一个域(因为是贴着右边走的),这个时候可以用叉积算下面积,如果面积为负,那么一定是贴着最外面走的,右边是无穷域。

所以我们得到了每条有向边右边的域,每条有向边在对偶图中也对应着一条有向边,我们不妨设对偶图中的有向边由i的右边的域指向i的左边的域。

然后对偶图就建好了。

其实思路很简单的

那么考虑一下这道题,我们以无穷域为根节点搜索出一棵dfs树

那么dfs树上的边在开发区域中肯定会进♂进♂出♂出,但是这样进♂进♂出♂出没有快感啊,怎么办呢

我们考虑求出dfs树上每个节点子树的权值和,那么进♂去的时候加上子树的权值和,出来的时候减去剩下子树的权值和,于是就得到答案(有快感)了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
int read(){char ch=getchar();int x=0,f=1;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;}
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
const int N=200000+5;
const int M=600000+5;
typedef long long ll;
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
struct point{ll x,y;}p[N];
point operator - (point a,point b){return (point){a.x-b.x,a.y-b.y};}
ll cross(point a,point b){return a.x*b.y-a.y*b.x;}
ll cross(point a,point b,point c){return cross(b-a,c-a);}
struct Edge{int to,next;}e[M<<1];
int head[N<<2];
struct edge{
	int u,v,ri;
	double slop;
	edge(){}
	edge(int _u,int _v):u(_u),v(_v),ri(0){
		slop=atan2(p[v].y-p[u].y,p[v].x-p[u].x);
	}
}o[M<<1];
void ins(int i){e[i]=(Edge){o[i^1].ri,head[o[i].ri]};head[o[i].ri]=i;}
vector<int>g[N];
bool cmp(int i,int j){return o[i].slop<o[j].slop;}
bool pmc(int i,int j){return o[i].v<o[j].v;}
int next[M<<1],q[M<<1];
ll area[N<<2],sum[N<<2][2];
int dual,root,fa[N<<2];
bool vis[N<<2],tree[M<<1];
void dfs(int u){
	vis[u]=true;
	if(u^root)sum[u][0]=area[u]*area[u],sum[u][1]=area[u]*2;
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].to;if(vis[v])continue;
		fa[v]=u;tree[i]=tree[i^1]=true;dfs(v);
		sum[u][0]+=sum[v][0];sum[u][1]+=sum[v][1];
	}
}
int find(int u,int v){
	int l=0,r=g[u].size()-1;
	while(l<=r){
		int mid=(l+r)/2;
		if(v<=o[g[u][mid]].v)r=mid-1;
		else l=mid+1;
	}
	return g[u][r+1];
}
int main(){
	//freopen("a.in","r",stdin);
	//freopen("a.out","w",stdout);
	int n,m,k;n=read();m=read();k=read();
	rep(i,1,n)p[i].x=read(),p[i].y=read();
	rep(i,1,m){
		int u,v;u=read();v=read();
		o[i<<1]=edge(u,v);o[i<<1|1]=edge(v,u);
		g[u].push_back(i<<1);g[v].push_back(i<<1|1);
	}
	rep(i,1,n){
		sort(g[i].begin(),g[i].end(),cmp);
		for(int j=0,k=g[i].size();j<k;j++)
		next[g[i][j]]=g[i][(j+1)%k];
		sort(g[i].begin(),g[i].end(),pmc);
	}
	rep(i,2,(m<<1|1))
	if(!o[i].ri){
		int j=i;dual++;q[0]=0;
		do o[j].ri=dual,q[++q[0]]=o[j].v,j=next[j^1];while(j^i);
		rep(j,2,q[0]-1)
		area[dual]+=cross(p[q[1]],p[q[j+1]],p[q[j]]);
		if(area[dual]<0)root=dual;
	}
	rep(i,2,(m<<1|1))ins(i);
	dfs(root);
	ll ans1=0,ans2=0;
	while(k--){
		q[0]=read();q[0]=(q[0]+ans1)%n+1;
		rep(i,1,q[0])q[i]=read(),q[i]=(q[i]+ans1)%n+1;
		ans1=0;ans2=0;
		rep(i,1,q[0]){
			int u=q[i],v=q[i%q[0]+1],k=find(u,v);
			if(tree[k]){
				if(fa[o[k].ri]==o[k^1].ri)
				ans1-=sum[o[k].ri][0],ans2-=sum[o[k].ri][1];
				else
				ans1+=sum[o[k^1].ri][0],ans2+=sum[o[k^1].ri][1];
			}
		}
		ll d=gcd(ans1,ans2);ans1/=d,ans2/=d;
		printf("%lld %lld\n",ans1,ans2);
	}
	return 0;
}


你可能感兴趣的:(BZOJ 4541: [Hnoi2016]矿区)