[BZOJ4541][平面图]HNOI2016:矿区

BZOJ4541

显然先转对偶图,然后我们考虑如何统计一个询问的答案

如果一个询问经过了一条边,那么就表示这条边在对偶图上对应的边下面的点或者上面的点都有被围起来,如何判断是下面还是上面?只需要看一下边的方向(查询对偶图上的祖先-儿子关系)即可,但是在一个图上并不好统计一条边"下面",“上面”的点

可以发现上述操作如果在任意一棵以最外面那个平面(面积为负)为根的生成树上进行,答案是不变的,所以就在生成树上统计,如果一条边是从父亲指向儿子,那就把儿子这整个子树的答案加进来(“下面”),如果是从儿子指向父亲,那就把儿子这个子树的答案减去(“上面”),然后就做完了

Code:

#include<bits/stdc++.h>
#define ll long long
#define db double
#define pb push_back
using namespace std;
inline char nc(){
    static char buf[1000000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
#define getchar nc
inline int read(){
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
const int N=5e5+5,M=6e5+5;
inline ll gcd(ll x,ll y){return y==0?x:gcd(y,x%y);}
struct point{
	ll x,y;
	point(){}
	point(int _x,int _y):x(_x),y(_y){}
	friend inline point operator + (const point &a,const point &b){return point(a.x+b.x,a.y+b.y);}
	friend inline point operator - (const point &a,const point &b){return point(a.x-b.x,a.y-b.y);}
	friend inline ll operator * (const point &a,const point &b){return a.x*b.y-a.y*b.x;}
}p[N];
struct E{
	int x,y,id;db ang;
	E(){}
	E(int _x,int _y,int _id):x(_x),y(_y),id(_id),ang(atan2((db)(p[y].y-p[x].y),(db)(p[y].x-p[x].x))){}
	friend inline bool operator < (const E &a,const E &b){return a.ang<b.ang;}
}e[M<<1];
vector<E>st[N],G[N<<1];
inline int Find(int x,const E &y){return lower_bound(st[x].begin(),st[x].end(),y)-st[x].begin();}
int id[M<<1],nxt[M<<1],q[N];
ll s1[N<<1],s2[N<<1];
int tot=0,cnt=1,rt;
inline void solve(){
	ll area;int now,start;
	for(int i=2;i<=cnt;i++) if(!id[i]){
		area=0;now=i;start=e[i].x,id[i]=++tot;
		while(1){
			int tmp=nxt[now];id[tmp]=tot;
			if(e[tmp].y==start) break;
			area+=(p[e[tmp].x]-p[start])*(p[e[tmp].y]-p[start]);
			now=tmp;
		}
		s1[tot]=area;s2[tot]=area*area;
		if(area<=0) rt=tot;
	}
	for(int i=2;i<=cnt;i++) G[id[i]].pb(E(id[i],id[i^1],i));
}
int pt[N<<1],flg[M<<1],fa[N<<1];
void dfs(int v){
	pt[v]=1;
	for(int i=0;i<G[v].size();i++){
		int y=G[v][i].y;
		if(pt[y]) continue;
		fa[y]=v;flg[G[v][i].id]=flg[G[v][i].id^1]=1;
		dfs(y);
		s1[v]+=s1[y],s2[v]+=s2[y];
	}
}
int main(){
	int n=read(),m=read(),Q=read();
	for(int i=1;i<=n;i++) p[i].x=read(),p[i].y=read();
	for(int x,y,i=1;i<=m;i++){
		x=read(),y=read();
		e[++cnt]=E(x,y,cnt);st[x].pb(e[cnt]);
		e[++cnt]=E(y,x,cnt);st[y].pb(e[cnt]);		
	}
	for(int i=1;i<=n;i++) sort(st[i].begin(),st[i].end());
	for(int i=2;i<=cnt;i++){
		int pos=Find(e[i].y,e[i^1])-1;
		if(pos<0) pos+=st[e[i].y].size();
		nxt[i]=st[e[i].y][pos].id;
	}
	solve();dfs(rt);ll ans1=0,ans2=0;
	while(Q--){
		int k=(read()+ans2)%n+1;
		for(int i=1;i<=k;i++) q[i]=(read()+ans2)%n+1;
		ans1=ans2=0;q[k+1]=q[1];
		for(int i=1;i<=k;i++){
			int x=q[i],y=q[i+1],tmp=st[x][Find(x,E(x,y,0))].id;
			if(!flg[tmp]) continue;
			if(id[tmp]==fa[id[tmp^1]]) ans1+=s1[id[tmp^1]],ans2+=s2[id[tmp^1]];
			else ans1-=s1[id[tmp]],ans2-=s2[id[tmp]];
		}
		if(ans1<0) ans1=-ans1,ans2=-ans2;
		ll d=gcd(ans1,ans2);ans1/=d,ans2/=d;
		if(ans2&1) ans1<<=1;
		else ans2>>=1;
		cout<<ans2<<" "<<ans1<<"\n";
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值