给定一个 N 个点 M 条边的平面图 G 。有 Q 个询问,每次询问平面图上的一个区域 A ,(逆时针地给定多边形的点集),你需要求出 ∑P⊂AS(P)2∑P⊂AS(P) , S(P) 表示 P 这个面的面积。
N≤2∗105,M≤3N−6
∑询问点数≤2∗106
首先我们要构成 G 的对偶图,设为 G′ ,以无穷域为根,构出 G′ 的生成树 T 。以无穷域为根是有原因的,等下会讲。
接下来,对于一个询问 A ,我们依次在 T 中将 A 中相邻点的连边在 G′ 中对应的对偶边删掉(假如不在生成树中则不管),接下来 T 会裂成若干个联通块,假如一个联通块被我们选出来的边围住了,那么我们就可以将这个联通块的答案统计进去了。但怎么定义围住了?设我们当前要删掉的边为 (u,v) ,注意边是有方向的,就是一个向量,那么假如在树中, u 为 v 的父亲,那么我们就加上 Subv 的信息,若 v 为 u 的父亲,那么我们减去 Subu 的信息。那么我们最终就可以得到所有合法联通块的信息了。
这样为什么是对的?
1. 不会计多。假如一个面 u 不在 A 中,但最后却被统计入了某个合法的联通块中,那么这个联通块中必然会存在一条边 (a,b) 满足 a 在 A 中而 b 不在 A 中,但这样的边我们一开始就会删掉,因此矛盾。
2. 不会计少。注意到我们唯一计少的情况就是存在一个面 u ,使得 u 在 A 中,但 u 不在任一合法的联通块中。注意到一个联通块的边界必然是由 A 的边界面所构成,而 u 在 A 中,那么 u 在生成树中至少与一个 A 中的边界面联通(把他围住了),所以 u 必然在某个联通块中,矛盾。
因此,我们就证明了这个算法是正确的。以无穷域为根就是因为无穷域不可能在 A 中,那么就能保证每个在 A 中的面在生成树中必然存在祖先边,也就是与某个边界面联通。
那么这题就比较简单了,一开始先构出对偶图,以无穷域为根做生成树,然后对于询问扫一下每条边即可。
由于构出对偶图需要每个点对每条边按极角排序,所以总的复杂度为 O(MlogM+∑询问点数)
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <map>
#define fe first
#define se second
using namespace std;
typedef unsigned long long LL;
typedef pair<int,int> P;
const int MAXN = 2000005,HAS_1 = 3999647,HAS_2 = 3983633;
LL operator ^(const P &a,const P &b) {return LL(a.fe) * b.se - LL(a.se) * b.fe;}
vector<int> Lk[MAXN],E[MAXN];
LL S[MAXN],Are[MAXN];
long long Ar[MAXN];
bool Walk[MAXN],In[MAXN];
double Ang[MAXN];
P Po[MAXN];
int To[MAXN],Bel[MAXN],Rank[MAXN],N,M,K,tot,cnt;
int V[HAS_1][3];
void Push(LL a,int ref)
{
int l = a % HAS_1;
for(;V[l][2];l = (l + 1) % HAS_1);
V[l][0] = a % HAS_1,V[l][1] = a % HAS_2,V[l][2] = ref;
}
int Find(LL a)
{
int l = a % HAS_1,l1 = a % HAS_2,ll = a % HAS_1;
for(;V[l][0] != ll || V[l][1] != l1;l = (l + 1) % HAS_1);
return V[l][2];
}
void Link(int u,int v)
{
To[++ tot] = v,Ang[tot] = atan2(Po[v].se - Po[u].se,Po[v].fe - Po[u].fe),Lk[u].push_back(tot);
Push(u * 1ll * N + v,tot);
}
inline bool cmp(const int &a,const int &b) {return Ang[a] < Ang[b];}
void Dfs(int Now,int Pr)
{
for(;!Walk[Now];)
{
Walk[Now] = 1;
Bel[Now] = cnt;
Ar[cnt] += (Po[To[Now]] ^ Po[Pr]);
Pr = To[Now];
Now = Lk[To[Now]][(Rank[Now ^ 1] + 1) % Lk[To[Now]].size()];
}
}
void Dfs_T(int Now)
{
static int Q[MAXN];
Q[1] = Now;
int fi = 1,en = 1;
for(;fi <= en;fi ++)
{
Now = Q[fi];
Walk[Now] = 1;
S[Now] = Are[Now] * Are[Now];
for(int i = 0;i < E[Now].size();i ++)
{
int v = E[Now][i];
if (Walk[Bel[v ^ 1]]) continue;
Walk[Bel[v ^ 1]] = 1;
In[v] = 1;
Q[++ en] = Bel[v ^ 1];
}
}
for(;en;en --)
{
Now = Q[en];
for(int i = 0;i < E[Now].size();i ++)
{
int v = E[Now][i];
if (!In[v]) continue;
S[Now] += S[Bel[v ^ 1]],Are[Now] += Are[Bel[v ^ 1]];
}
}
}
LL Gcd(LL a,LL b) {return b ? Gcd(b,a % b) : a;}
int Search(int u,int v) {return Find(u * 1ll * N + v);}
template<class T>
void read(T &x)
{
char c;
bool f = 0;
while (c = getchar(),(c < '0' || c > '9') && c != '-');
if (c == '-') f = 1,x = 0; else x = c - 48;
while (c = getchar(),(c >= '0' && c <= '9')) x = x * 10 + c - 48;
if (f) x = -x;
}
int main()
{
scanf("%d%d%d", &N, &M, &K);
for(int i = 1;i <= N;i ++) read(Po[i].fe),read(Po[i].se);
tot = 1;
for(int i = 1;i <= M;i ++)
{
int u,v;
read(u),read(v);
Link(u,v),Link(v,u);
}
for(int i = 1;i <= N;i ++) stable_sort(Lk[i].begin(),Lk[i].end(),cmp);
for(int i = 1;i <= N;i ++)
for(int j = 0;j < Lk[i].size();j ++) Rank[Lk[i][j]] = j;
for(int i = 1;i <= N;i ++)
for(int j = 0;j < Lk[i].size();j ++)
{
int v = Lk[i][j];
if (Walk[v]) continue;
++ cnt;
Dfs(v,i);
}
for(int i = 2;i <= tot;i ++)
{
int u = To[i],v = To[i ^ 1];
E[Bel[i]].push_back(i);
}
memset(Walk,0,sizeof Walk);
int Root = 0;
for(int i = 1;i <= cnt;i ++) if (Ar[i] < 0) Root = i,Ar[i] = 0;
for(int i = 1;i <= cnt;i ++) Are[i] = Ar[i];
Dfs_T(Root);
LL lst = 0;
for(int i = 1;i <= K;i ++)
{
static long long Z[MAXN];
long long C = 0;
read(C);
C = (C + lst) % N + 1;
for(int j = 1;j <= C;j ++)
{
read(Z[j]);
Z[j] = (Z[j] + lst) % N + 1;
}
LL Anstop = 0,Ansbot = 0;
bool ok = 0;
for(int j = 1;j <= C;j ++)
{
int u = Z[j],v = Z[j % C + 1];
int ref = Search(u,v);
if (!(In[ref] || In[ref ^ 1])) continue;
if (In[ref]) ok = 1,Anstop += S[Bel[ref ^ 1]],Ansbot += Are[Bel[ref ^ 1]]; else
Anstop -= S[Bel[ref]],Ansbot -= Are[Bel[ref]];
}
Ansbot *= 2;
LL gcd = Gcd(Anstop,Ansbot);
Anstop /= gcd,Ansbot /= gcd;
printf("%lld %lld\n", Anstop,Ansbot);
lst = Anstop % N;
}
return 0;
}