题目链接
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;
}