1. 程式人生 > >BZOJ 4541: [Hnoi2016]礦區

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;
}