[HNOI2016]矿区
题面
给定平面上许多的区域,每次询问一些点所包围的子区域的面积平方之和与总面积之和之比,强制在线。
\(\text{Solution}\)
毒瘤至极
显然我们要维护每一个小区域的面积,由于我们要维护的是一个面,所以我们将平面图转对偶图(不会的戳这里)。面的信息就缩在一个点中,以无穷域为根随便弄出个生成树,对于任意点x处理出以x为根的子树所代表的区域的面积。
那么对于一组询问,我们扫描它的每一条边,看它的反向边是否是它的父亲,如果是,则加上自己子树的面积,否则减去子树的面积。
显然这么毒瘤的题我是码不出的。
不过如果之前学过对偶图,思维难度并不大。
#include <set>
#include <vector>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <assert.h>
#include <algorithm>
using namespace std;
#define LL long long
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define GO debug("GO\n")
inline int rint() {
register int x = 0, f = 1; register char c;
while (!isdigit(c = getchar())) if (c == '-') f = -1;
while (x = (x << 1) + (x << 3) + (c ^ 48), isdigit(c = getchar()));
return x * f;
}
template<typename T> inline void chkmin(T &a, T b) { a > b ? a = b : 0; }
template<typename T> inline void chkmax(T &a, T b) { a < b ? a = b : 0; }
const int N = 2e5 + 10, M = 1.2e6 + 5;
const double eps = 1e-10;
#define Iter vector<Edge>::iterator
LL ans1, ans2;
int n, m, Q, tot = 1, cnt, root;
struct vec {
int x, y;
vec(int _x = 0, int _y = 0) {x = _x, y = _y;}
vec operator- (const vec &b) const
{ return vec(x - b.x, y - b.y); }
LL operator* (const vec &b) const
{ return 1ll * x * b.y - 1ll * y * b.x;}
} P[N];
struct Edge {
int id, u, v; double angle;//atan2 rad
bool operator< (const Edge &b) const
{ return fabs(angle - b.angle) < eps ? v < b.v : angle < b.angle; }
} E[M];
int nxt[M], pos[M], fa[M], vis[M], intree[M], ask[M];
LL s[M], ss[M];
vector<Edge> G[N], tree[M];
void add(int u, int v) {
E[++tot] = (Edge) {tot, u, v, atan2(P[v].y - P[u].y, P[v].x - P[u].x)};
G[u].push_back(E[tot]);
}
void Build() {
for (int i = 1; i <= n; ++ i)
sort(G[i].begin(), G[i].end());
for (int i = 2; i <= tot; ++ i) {
int v = E[i].v;
Iter it = lower_bound(G[v].begin(), G[v].end(), E[i ^ 1]);
if (it == G[v].begin()) it = G[v].end();
--it;
nxt[i] = it->id;
}
for (int i = 2; i <= tot; ++ i) {
if (pos[i]) continue;
pos[i] = pos[nxt[i]] = ++cnt;
for (int j = nxt[i]; E[j].v != E[i].u; j = nxt[j], pos[j] = cnt)
s[cnt] += (P[E[j].u] - P[E[i].u]) * (P[E[j].v] - P[E[i].u]);
if (s[cnt] <= 0) root = cnt;
}
for (int i = 2; i <= tot; ++ i)
tree[pos[i]].push_back((Edge){i, pos[i], pos[i ^ 1]});
}
void DFS(int u, int las) {
fa[u] = las;
ss[u] = s[u] * s[u];
s[u] <<= 1;
vis[u] = 1;
for (int i = 0; i < tree[u].size(); ++ i) {
int v = tree[u][i].v;
if (vis[v]) continue;
intree[tree[u][i].id] = intree[tree[u][i].id ^ 1] = 1;
DFS(v, u);
s[u] += s[v];
ss[u] += ss[v];
}
}
void solve() {
while (Q --) {
int tmp = rint(); tmp = (tmp + ans1) % n + 1;
for (int i = 1; i <= tmp; ++ i)
ask[i] = (rint() + ans1) % n + 1;
ask[tmp + 1] = ask[1];
ans1 = ans2 = 0;
for (int i = 1; i <= tmp; ++ i) {
int x = ask[i], y = ask[i + 1];
Edge xhc = (Edge) {0, x, y, atan2(P[y].y - P[x].y, P[y].x - P[x].x)};
Iter it = lower_bound(G[x].begin(), G[x].end(), xhc);
int id = it->id;
if (!intree[id]) continue;
if (fa[pos[id]] == pos[id ^ 1])
ans1 += ss[pos[id]], ans2 += s[pos[id]];
else
ans1 -= ss[pos[id ^ 1]], ans2 -= s[pos[id ^ 1]];
}
LL gcd = __gcd(ans1, ans2);
ans1/=gcd, ans2/=gcd;
printf("%lld %lld\n", ans1, ans2);
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("xhc.in", "r", stdin);
freopen("xhc.out", "w", stdout);
#endif
n = rint(), m = rint(), Q = rint();
for (int i = 1; i <= n; ++ i) {
int u = rint(), v = rint();
P[i] = vec(u, v);
}
for (int i = 1; i <= m; ++ i) {
int x = rint(), y = rint();
add(x, y), add(y, x);
}
Build();
DFS(root, 0);
solve();
}