[BZOJ4541] [HNOI2016] 矿区

题目链接

BZOJ:https://lydsy.com/JudgeOnline/problem.php?id=4541.

LOJ:https://loj.ac/problem/2052.

洛谷:https://www.luogu.org/problemnew/show/P3249.

Solution

平面图转对偶图。

平面图转对偶图大概就是说,对于一个平面图,它把平面分割成了若干个区块,对于每个区块抽象成一个点,原图上的分割两个区块的边转化成连接两个点的边,转化完之后的图就叫对偶图。

对于这道题,我们先把对偶图弄出来,随便整一棵生成树,对于每个点我们记录当前区块的面积的平方。

然后对于询问我们逆时针扫描这个图,然后对经过的边分析,如果当前边为非树边就不管,否则我们可以随便定义一个正方向,如果当前边是儿子指向父亲则ans+=s[son](s[x])表示(x)子树的点权和,否则ans-=s[son]

你画个图就能明白了,具体的我也不会证

具体见代码

#include<bits/stdc++.h>
using namespace std;

void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}

void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('
');}

#define lf double
#define ll long long 

const int maxn = 2e6+10;
const int inf = 1e9;
const lf eps = 1e-8;

struct P {
	int x,y;
	P operator - (const P &rhs) const {return (P){x-rhs.x,y-rhs.y};}
	ll operator * (const P &rhs) const {return 1ll*x*rhs.y-1ll*y*rhs.x;}
}p[maxn];

ll gcd(ll a,ll b) {return !b?a:gcd(b,a%b);}

struct edge{
	int u,v,id;lf ang;
	edge() {}
	edge(int _u,int _v) {u=_u,v=_v,ang=atan2(p[v].y-p[u].y,p[v].x-p[u].x);}
	bool operator < (const edge &rhs) const {return ang<rhs.ang;}
}a[maxn];

vector <edge > e[maxn];

int n,m,Q,nxt[maxn],tot=1,bel[maxn],cnt,rt,f[maxn],in[maxn],tr[maxn];
ll s2[maxn],ans,val[maxn];

int find(int x) {return f[x]==x?x:f[x]=find(f[x]);}

void ins(int u,int v) {a[++tot]=edge(u,v),a[tot].id=tot;e[u].push_back(a[tot]);}

struct Tree {
	int head[maxn],tot,fa[maxn];
	struct Edge{int to,nxt;}e[maxn<<1];

	void add(int u,int v) {e[++tot]=(Edge){v,head[u]},head[u]=tot;}
	void ins(int u,int v) {add(u,v),add(v,u);}
	
	void dfs(int x,int F) {
		fa[x]=F,s2[x]=val[x];
		for(int i=head[x];i;i=e[i].nxt)
			if(e[i].to!=fa[x]) dfs(e[i].to,x),s2[x]+=s2[e[i].to];
	}
}T;

void prepare() {
	for(int i=1;i<=n;i++) sort(e[i].begin(),e[i].end());  //极角排序
	for(int i=2;i<=tot;i++) {   //nxt[i]表示当前走到第i条边,下一条边走哪条
		int v=a[i].v;
		int t=lower_bound(e[v].begin(),e[v].end(),edge(a[i].v,a[i].u))-e[v].begin()-1;
		if(t==-1) t=e[v].size()-1;
		nxt[i]=e[v][t].id;
	}
	for(int i=2;i<=tot;i++) {
		if(bel[i]) continue;
		int now=nxt[i];ll s=0;bel[i]=bel[now]=++cnt;
		s+=(p[a[now].u]-p[a[i].u])*(p[a[now].v]-p[a[i].u]);
		while(a[now].v!=a[i].u) {
			now=nxt[now];
			bel[now]=cnt;s+=(p[a[now].u]-p[a[i].u])*(p[a[now].v]-p[a[i].u]);
		}if(s<=0) rt=cnt;
		val[cnt]=s*s; //绕着多边形转一圈,当前多边形标号为cnt
	}
	for(int i=1;i<=cnt;i++) f[i]=i;
	for(int i=2;i<=tot;i+=2) {
		int u=find(bel[i]),v=find(bel[i^1]);
		if(u!=v) f[u]=v,T.ins(bel[i],bel[i^1]),tr[i]=tr[i^1]=1;
	}
	T.dfs(rt,0);
}

void solve() {
	int k;read(k);k=(1ll*k+ans)%n+1;
	for(int i=1;i<=k;i++) read(in[i]),in[i]=(1ll*in[i]+ans)%n+1;in[k+1]=in[1];
	ans=0;ll res=0;
	for(int i=1;i<=k;i++) {
		int r=in[i],t=lower_bound(e[r].begin(),e[r].end(),edge(in[i],in[i+1]))-e[r].begin();
		res+=(p[r]-p[in[1]])*(p[in[i+1]]-p[in[1]]);
		int id=e[r][t].id;
		if(!tr[id]) continue;
		if(bel[id]==T.fa[bel[id^1]]) ans-=s2[bel[id^1]];
		else if(T.fa[bel[id]]==bel[id^1]) ans+=s2[bel[id]];
	}ans=abs(ans),res=abs(res);res<<=1;
	ll t=gcd(ans,res);ans/=t,res/=t;
	printf("%lld %lld
",ans,res);
}

int main() {
	read(n),read(m),read(Q);
	for(int i=1;i<=n;i++) read(p[i].x),read(p[i].y);
	for(int i=1,x,y;i<=m;i++) read(x),read(y),ins(x,y),ins(y,x);
	prepare();
	for(int i=1;i<=Q;i++) solve();
	return 0;
}
原文地址:https://www.cnblogs.com/hbyer/p/10636773.html